关于c#:如何公开集合属性?

关于c#:如何公开集合属性?

How to expose a collection property?

每次我创建一个具有collection属性的对象时,我都会以最佳方式来回移动吗?

  • 带有吸气剂的公共财产
    返回对私有变量的引用
  • 明确的get_ObjList和set_ObjList
    返回并创建新的或克隆的方法
    每次对象
  • 明确的get_ObjList返回一个
    IEnumerator和一个set_ObjList
    需要IEnumerator
  • 如果集合是数组(即objList.Clone())与列表相比,会有所不同吗?

    如果返回实际的集合作为引用非常糟糕,因为它创建了依赖关系,那么为什么要返回任何属性作为参考呢?每当您将子对象作为参考公开时,就可以更改该子对象的内部,而无需父"知道",除非该子对象具有属性更改事件。有内存泄漏的风险吗?

    而且,选项2和3不会中断序列化吗?这是一个陷阱22还是您必须在拥有collection属性的任何时候实现自定义序列化?

    通用的ReadOnlyCollection似乎是通用的不错的折衷方案。它包装一个IList并限制对其的访问。也许这有助于内存泄漏和序列化。但是它仍然有枚举的问题

    也许只是视情况而定。如果您不关心集合是否被修改,则只需按#1将其公开为私有变量上的公共访问器即可。如果您不希望其他程序修改集合,则#2和/或#3更好。

    问题的隐含含义是,为什么应该在一种方法之上使用另一种方法,以及在安全性,内存,序列化等方面的后果是什么?


    公开集合的方式完全取决于用户与之交互的方式。

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

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

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

    WPF还公开了一些可设置的集合,以允许用户显示他们控制的项的集合,例如ItemsControl上的ItemsSource属性(原始问题的选项#3)。但是,这不是常见的用例。


    2)如果用户将仅读取对象维护的数据,则可以使用只读集合,如Quibblesome建议的那样:

    1
    2
    3
    4
    5
    6
    7
    8
    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 >,或者要限制对更高级用户的访问,则可以通过枚举器包装对集合的访问:

    1
    2
    3
    4
    5
    6
    public IEnumerable< T > MyCollection {
      get {
        foreach( T item in this.myPrivateCollection_ )
          yield return item;
      }
    }

    这种方法易于实现,并且无需暴露内部集合即可提供对所有成员的访问。但是,这确实需要使集合保持未修改状态,因为如果在修改集合后尝试枚举集合,则BCL集合类将引发异常。如果基础集合可能会更改,则可以创建一个轻包装器来安全地枚举该集合,也可以返回该集合的副本。


    3)最后,如果您需要公开数组而不是更高级别的集合,则应返回该数组的副本以防止用户对其进行修改(原始问题的选项2):

    1
    2
    3
    4
    5
    6
    7
    8
    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 )方法,也可以使用自定义索引器:

    1
    2
    3
    4
    5
    6
    7
    public T this[int index] {
      get { return this.myArray_[index]; }
      set {
        // TODO: validate new value; raise change event; etc.
        this.myArray_[index] = value;
      }
    }

    (当然,通过实现自定义索引器,您将复制BCL类的工作:)


    我通常会这样做,它是一个返回System.Collections.ObjectModel.ReadOnlyCollection的公共获取器:

    1
    2
    3
    4
    5
    6
    7
    public ReadOnlyCollection<SomeClass> Collection
    {
        get
        {
             return new ReadOnlyCollection<SomeClass>(myList);
        }
    }

    并在对象上修改公共方法的集合。

    1
    2
    Clear();
    Add(SomeClass class);

    如果该类被认为是供其他人使用的存储库,那么我只是按照方法1公开私有变量,因为它节省了编写自己的API的麻烦,但是我倾向于在生产代码中避免使用它。


    ReadOnlyCollection仍然有一个缺点,那就是用户无法确定原始集合不会在不适当的时候被更改。相反,您可以使用不可变集合。如果您需要进行更改,则可以更改原件,而不是更改原件。它的实现方式与可变集合的性能具有竞争力。甚至更好,如果您不必多次复制原始文件,然后再对每个副本进行许多不同(不兼容)的更改。


    我建议使用新的IReadOnlyList< T >IReadOnlyCollection< T >接口公开集合(需要.NET 4.5)。

    例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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 >或新的Immutable集合。

    避免使用接口IEnumerable< T >公开集合。
    此接口没有定义任何保证多个枚举性能良好的保证。如果IEnumerable表示查询,则每个枚举均再次执行查询。获得IEnumerable实例的开发人员不知道它是表示集合还是查询。

    有关此主题的更多信息,可以在此Wiki页面上阅读。


    您为什么建议使用ReadOnlyCollection(T)是一个折衷方案?如果仍然需要在原始包装的IList上获得更改通知,则还可以使用ReadOnlyObservableCollection(T)来包装您的集合。在您的方案中,这会不会是一个妥协?


    我是Java开发人员,但我认为这对于c#是相同的。

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


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


    推荐阅读

      扬声器属性级别设置|扬声器属性高级

      扬声器属性级别设置|扬声器属性高级,,1. 扬声器属性高级选择“高级”标签试试,不行的话,说明系统有问题了,直接换个验证过的系统盘重装系统就

      16天的最新的内存引用:4gb上涨10元

      16天的最新的内存引用:4gb上涨10元,,内存价格上涨到4GB 10元 在今天中关村的现货市场,内存价格总体呈发展趋势,但它是小的。内存市场昨日下

      wps如何生成引用

      wps如何生成引用,WPS教程,1.wps怎么添加引用文献wps添加引用文献:1、打开文档,点击WPS文字右侧下拉菜单。2、打开插入>引用>脚注与尾注3、选

      Python之可迭代对象、迭代器、生成器

      Python之可迭代对象、迭代器、生成器,迭代,生成器,一、概念描述可迭代对象就是可以迭代的对象,我们可以通过内置的iter函数获取其迭代器,可

      快捷键打开软件|快捷键打开软件属性

      快捷键打开软件|快捷键打开软件属性,,快捷键打开软件属性1.首先打开PS,鼠标左键单击“编辑”;2.在弹出的窗口找“键盘快捷键”,如果没有点击

      应用程序对象

      应用程序对象,,应用程序对象是一个应用程序级对象,用于在所有用户之间共享信息,并且在Web应用程序运行期间可以保存数据。 应用的性质: 方法