代码之家  ›  专栏  ›  技术社区  ›  jedatu

如何公开集合属性?[闭门]

  •  45
  • jedatu  · 技术社区  · 18 年前

    每次我创建一个具有集合属性的对象时,我都会在最佳方式上来回移动?

    1. 返回对私有变量的引用
    2. 显式获取对象列表和设置对象列表
    3. 返回 IEnumerator和一个集合对象列表 电子计算器

    如果集合是一个数组(即objList.Clone())而不是一个列表,这会有区别吗?

    还有,选项2和3不破坏序列化吗?这是第22条军规,还是在拥有集合属性时必须实现自定义序列化?

    通用的ReadOnlyCollection似乎是一个很好的通用折衷方案。它包装IList并限制对它的访问。也许这有助于解决内存泄漏和序列化问题。然而,它仍然有 enumeration concerns

    这个问题隐含着为什么一种方法要优于另一种,对安全性、内存、序列化等有什么影响。?

    7 回复  |  直到 18 年前
        1
  •  56
  •   Community Mohan Dere    9 年前

    如何公开集合完全取决于用户打算如何与其交互。

    如果用户将从对象集合中添加和删除项,则最好使用简单的仅获取集合属性(原始问题中的选项#1):

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

    Items WindowsForms和WPF上的集合 ItemsControl 控件,用户在其中添加和删除希望控件显示的项。这些控件发布实际集合,并使用回调或事件侦听器跟踪项。

    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;
      }
    }
    

        2
  •  3
  •   Quibblesome    18 年前

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

    和对象上的公共方法来修改集合。

    Clear();
    Add(SomeClass class);
    

    如果该类被认为是一个供其他人使用的存储库,那么我只需按照方法1公开私有变量,因为这样可以节省编写自己的API的时间,但我倾向于在生产代码中回避这一点。

        3
  •  1
  •   Johannes    11 年前

    Immutable Collections . 如果您需要进行更改,则会给您一份修改后的副本,而不是更改原件。它的实现方式与可变集合的性能具有竞争力。或者,如果您不必多次复制原始文件以对每个副本进行多次不同(不兼容)的更改,则更好。

        4
  •  1
  •   jbe    10 年前

    我建议使用新的 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

        5
  •  0
  •   Ryan Duffield    18 年前

    如果您只是想在实例上公开一个集合,那么对私有成员变量使用getter/setter对我来说似乎是最明智的解决方案(您提出的第一个选项)。

        6
  •  0
  •   jpierson    17 年前

    ReadOnlyObservableCollection(T) 来包装你的收藏。在您的场景中,这是否不是一种妥协?

        7
  •  0
  •   Patrick D'Souza ob1    13 年前

    我从不公开私有集合属性,因为程序的其他部分可以在没有父级注意的情况下更改它,因此在getter方法中,我返回一个包含集合对象的数组,在setter方法中,我调用 clearAll() addAll()