2008-08-29 73 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