2008-08-29 43 views
43

每次我創建一個具有集合屬性的對象時,我都會回到最佳方式來實現它?如何顯示集合屬性?

  1. 公共財產與 返回私有變量的引用
  2. 明確get_ObjList和set_ObjList 方法,返回,創造新的或克隆 對象每次吸氣
  3. 明確get_ObjList返回一個 的IEnumerator和一個set_ObjList 需要IEnumerator

它是否有所不同,如果集合是一個數組(即,objList.Clone())與一個List?

如果返回實際集合作爲參考非常糟糕,因爲它創建了依賴關係,那麼爲什麼要返回任何屬性作爲參考?任何時候當你暴露一個子對象作爲參考時,那個孩子的內部可以在沒有父母「知道」的情況下被改變,除非孩子有一個屬性改變的事件。是否有內存泄漏的風險?

而且,不要選擇2和3打破序列化?這是一個捕獲22還是你有任何時候實現自定義序列化你有一個集合屬性?

通用ReadOnlyCollection看起來像一個很好的折衷一般使用。它包裝了一個IList並限制對它的訪問。也許這有助於內存泄漏和序列化。但它仍然有enumeration concerns

也許它只是取決於。如果您不關心集合是否被修改,那麼只需將其公開爲每個#1的私有變量的公共訪問者。如果你不想讓其他程序修改集合,那麼#2和/或#3會更好。

問題的隱含意義是爲什麼一種方法應該用於另一種方法,以及安全性,內存,序列化等方面的後果是什麼?

回答

52

你如何公開一個集合完全取決於用戶如何打算與它進行交互。

1)如果用戶將被添加和從對象的集合中刪除項目,那麼一個簡單的只得到收集屬性是最好的(選擇#1從原來的問題):

private readonly Collection<T> myCollection_ = new ...; 
public Collection<T> MyCollection { 
    get { return this.myCollection_; } 
} 

這一戰略用於WindowsForms和WPF ItemsControl控件上的Items集合,其中用戶添加和刪除他們希望控件顯示的項目。這些控件發佈實際的集合並使用回調或事件偵聽器來跟蹤項目。

WPF還公開了一些可設置的集合,以允許用戶顯示它們控制的項目的集合,例如ItemsControl(原始問題中的選項#3)上的ItemsSource屬性。但是,這不是一個常見的用例。


2)如果用戶將只讀取由對象維護的數據,那麼你可以使用一個只讀集合,爲Quibblesome建議:

private readonly List<T> myPrivateCollection_ = new ...; 
private ReadOnlyCollection<T> myPrivateCollectionView_; 
public ReadOnlyCollection<T> MyCollection { 
    get { 
    if(this.myPrivateCollectionView_ == null) { /* lazily initialize view */ } 
    return this.myPrivateCollectionView_; 
    } 
} 

注意ReadOnlyCollection<T>提供了一個實時取景基礎集合,所以你只需要創建一次視圖。

如果內部收集未實現IList<T>,或者如果你想限制訪問更高級的用戶,可以改爲通過枚舉包裝集合訪問:

public IEnumerable<T> MyCollection { 
    get { 
    foreach(T item in this.myPrivateCollection_) 
     yield return item; 
    } 
} 

這種方法實現起來很簡單並且還提供了訪問所有成員而不暴露內部收藏。但是,它確實要求集合保持未修改狀態,因爲如果嘗試在修改集合後枚舉集合,則BCL集合類將引發異常。如果底層集合可能發生變化,您可以創建一個輕型包裝器來安全地枚舉集合,或返回集合的副本。


3)最後,如果你需要公開的陣列,而不是更高級別的集合,那麼你應該返回數組,以防止用戶從原單的問題修改它(選擇#2副本):

private T[] myArray_; 
public T[] GetMyArray() { 
    T[] copy = new T[this.myArray_.Length]; 
    this.myArray_.CopyTo(copy, 0); 
    return copy; 
    // Note: if you are using LINQ, calling the 'ToArray()' 
    // extension method will create a copy for you. 
} 

你不應該通過屬性暴露底下陣列,因爲你將不能夠告訴當用戶修改它。爲了讓修改數組,你可以添加相應的SetMyArray(T[] array)方法,或者使用自定義索引:

public T this[int index] { 
    get { return this.myArray_[index]; } 
    set { 
    // TODO: validate new value; raise change event; etc. 
    this.myArray_[index] = value; 
    } 
} 

(當然,通過實現自定義索引,你會複製BCL類的工作:)

0

如果你只是想在你的實例上公開一個集合,那麼使用私有成員變量的getter/setter似乎是對我來說最明智的解決方案(你的第一個建議選項)。

3

我經常去這個,一個公共的getter返回System.Collections.ObjectModel.ReadOnlyCollection:

public ReadOnlyCollection<SomeClass> Collection 
{ 
    get 
    { 
     return new ReadOnlyCollection<SomeClass>(myList); 
    } 
} 

和對象上公開的方法來修改該集合。

Clear(); 
Add(SomeClass class); 

如果類應該是別人亂用,然後我就暴露私有變量按照方法#1,因爲它可以節省編寫自己的API存儲庫,但我傾向於迴避這種美麗在生產代碼中。

+1

這會在每次讀取Collection屬性時創建新的ReadOnlycollection,這可能非常耗費資源 – Ivan 2009-09-04 16:00:54

+0

Aye,Emperor XLII改進了他發佈的上述示例中的前提。 – Quibblesome 2009-09-11 14:25:49

0

我是一個java開發人員,但我認爲這是相同的c#。

我從不公開私有集合屬性,因爲程序的其他部分可以在沒有父注意的情況下更改它,所以在getter方法中,我返回一個包含集合對象的數組,並在setter方法中我稱之爲clearAll()該集合然後addAll()

0

爲什麼你建議使用ReadOnlyCollection(T)是一個妥協?如果您仍然需要獲取原始包裝的IList上的更改通知,則還可以使用ReadOnlyObservableCollection(T)來包裝您的收藏。這會減少你的情況下的妥協嗎?

0

ReadOnlyCollection仍然有一個缺點,即消費者不能確定原始集合不會在不合時宜的情況下被更改。相反,您可以使用Immutable Collections。如果你需要做一個改變,那麼改變原來的你將得到一個修改後的副本。它被實現的方式與可變集合的性能相競爭。或者,如果您不需要多次複製原始文件以便在每個副本之後進行一系列不同(不兼容)的更改,那麼情況會更好。

0

我推薦使用新的IReadOnlyList<T>IReadOnlyCollection<T>接口來公開一個集合(需要.NET 4.5)。

例子:

public class AddressBook 
{ 
    private readonly List<Contact> contacts; 

    public AddressBook() 
    { 
     this.contacts = new List<Contact>(); 
    } 

    public IReadOnlyList<Contact> Contacts { get { return contacts; } } 

    public void AddContact(Contact contact) 
    { 
     contacts.Add(contact); 
    } 

    public void RemoveContact(Contact contact) 
    { 
     contacts.Remove(contact); 
    } 
} 

如果需要保證收集不能從被操縱外面再考慮ReadOnlyCollection<T>或新的不可變的集合。使用接口IEnumerable<T>來公開一個集合。 此接口沒有定義任何保證多枚枚舉表現良好。如果IEnumerable表示查詢,那麼每個枚舉都會再次執行該查詢。獲得IEnumerable實例的開發人員不知道它是否代表集合或查詢。

有關此主題的更多信息,請閱讀此Wiki page