【问题标题】:Correct usage of properties when dealing with a collection处理集合时正确使用属性
【发布时间】:2009-05-20 13:31:07
【问题描述】:

我想知道在处理集合时使用属性的最佳方式是什么。

例如,我有一个 Foo 类,我想存储该类的列表。应该使用以下哪一项:

private List<Foo> myList;
private List<Foo> myOtherList = new List<Foo>();

现在是房产:

public List<Foo> ListOfFoo
  {
     get 
     { 
        return myList; 
     }
     set 
     { 
        myList= new List<Foo>(value); 
     }
  }

还是应该只设置值?

public List<Foo> ListOfFoo
  {
     get 
     { 
        return myList; 
     }
     set 
     { 
        myList= value; 
     }
  }

【问题讨论】:

  • 集合应该可以从外部设置的原因是什么?
  • 这个属性是在一个自定义控件上进行的,它只允许对列表中的 Foo 对象进行拖放功能。我计划查询数据库以获取对象列表。每个控件可以有相同或不同的列表。

标签: c# collections properties


【解决方案1】:

选择

private List<Foo> myOtherList = new List<Foo>();

因为另一个只是声明了一个引用(设置为 null),所以上面的示例声明了一个对列表的引用,创建了一个列表并将该新列表分配给该引用。

选择

public List<Foo> ListOfFoo
  {
     get { return myList; }
     set { myList= new List<Foo>(value); }
  }

当您希望 myList 不反映列表分配给 myList 后发生的任何更改时,例如

List<string> myFirstList = new List<string>();
myFirstList.Add("Hello");
myFirstList.Add("World");

List<string> mySecondList = new List<string>(myFirstList); 
// mySecondList now contains Hello & world

myFirstList.Add("Boyo");
// myFrist List now contains Hello, world & Boyo
// mySecondList still contains Hello & world

选择

public List<Foo> ListOfFoo
  {
     get { return myList; }
     set { myList= value; }
  }

当您希望两个引用都指向同一个对象时,例如

List<string> myFirstList = new List<string>();
myFirstList.Add("Hello");
myFirstList.Add("World");

List<string> mySecondList = myFirstList; 
// mySecondList now contains Hello & world

myFirstList.Add("Boyo");
// myFrist List now contains Hello, world & Boyo
// mySecondList "also" contains Hello, world & Boyo 

上面的“也”用引号引起来,因为实际上只有一个列表,而且我的第一个和第二个都指向同一个列表。

【讨论】:

    【解决方案2】:

    通常您不想使用像List&lt;T&gt; 这样的丰富类型的属性(通常会使用Collection&lt;T&gt;),并且通常集合类型的属性是只读的 - 集合本身可以使用方法进行修改比如ClearAdd 等,这通常就足够了。

    例如:

    class Foo
    {
        Collection<Bar> _bars = new Collection<Bar>();
    
        public Collection<Bar> Bars { get { return _bars; } }
    }
    

    这也使您可以通过实现Collection&lt;T&gt; 的后代并覆盖InsertItemSetItem 等方法来验证对集合的修改。

    【讨论】:

    • 虽然在一般情况下您可能不想使用 List,但这样做是完全可以的。例如,如果您有一个内部数据对象。
    • siz - +1,我仍然会使用尽可能小的外部接口(例如 IList)
    • peterchen - Collection 比 IList 具有更广泛、更易于使用的接口,并且 Collection 可以将 IList 实现包装为构造函数。恕我直言,没有任何借口可以使用 IList 而不是 Collection
    • 在公共 API 的情况下,是的,我会使用尽可能小的接口。但是对于我用来通过 Web 服务传输数据的一系列数据对象,例如,我喜欢这些对象是漂亮的转储并且能够使用 List 中的方法,特别是因为我仍然在 .net 中开发2.0/3.0 并且无权访问 LINQ 扩展方法。
    【解决方案3】:

    视情况而定。

    当使用第一种样式时,您会创建列表的副本,这通常是不必要的。 .Net 约定是让设置者将引用分配给属性。这就是为什么我会倾向于第二种选择。

    但是,如果您打算进行复制操作,那么第一个选项就是您要查找的内容。

    【讨论】:

      【解决方案4】:

      一般来说,只公开一个接口(ICollection、IList 或类似的),并将其设为只读:

      private IList<Foo> m_list = new List<Foo>();
      public IList<Foo> List {get { return m_list; } }
      

      优点:您可以修改实现,例如从列表切换到可观察列表。您可能需要将 m_list 成员设为具体类型而不是接口,例如使用额外的功能。

      使用可设置的外部列表,您会遇到一些问题。但是,在某些情况下需要这样做:

      • 数据可以在外部创建,并且可能很大,并且经常更改(例如数万个项目)
      • 外部列表应在不同实例之间共享

      【讨论】:

        【解决方案5】:

        为什么不在类上使用 IEnumerator-Interface,如果必须使用 setter,请使用特定方法。

        这样你也隐藏了实际的列表实现。

        class FooBar : IEnumerator
        {
          private Collection<Foo> col;
        
          public IEnumarator GetEnumerator()
          {
            return col.GetEnumerator();
          }
        
          public void SetList(Collection col)
          {
            this.col= col; // you can also make this more general and convert the parameter so it fits your listimpl.
          }
        }
        
        class Clazz
        {
          private void WhatEver(){
            FooBar foobar = new FooBar();
            ...
            foreach(Foo f in foobar)
            {...}
          }
        }
        

        【讨论】:

          猜你喜欢
          • 2010-09-20
          • 1970-01-01
          • 1970-01-01
          • 2011-02-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多