45

在 C# 泛型之前,每个人都会通过创建一个实现 IEnumerable 的集合库来为他们的业务对象编写集合

IE:

public class CollectionBase : IEnumerable

然后会从中派生出他们的业务对象集合。

public class BusinessObjectCollection : CollectionBase

现在有了通用列表类,有没有人用它来代替?我发现我使用了两种技术的折衷方案:

public class BusinessObjectCollection : List<BusinessObject>

我这样做是因为我喜欢使用强类型名称,而不仅仅是传递列表。

方法是什么?

4

18 回答 18

49

我通常只是直接使用 List 的阵营,除非出于某种原因我需要封装数据结构并提供其功能的有限子集。这主要是因为如果我没有特定的封装需求,那么这样做只是浪费时间。

但是,使用 C# 3.0 中的聚合初始化功能,在一些新情况下我会提倡使用自定义集合类。

基本上,C# 3.0 允许任何实现IEnumerable并具有 Add 方法的类使用新的聚合初始值设定项语法。例如,因为 Dictionary 定义了 Add(K key, V value) 方法,所以可以使用以下语法初始化字典:

var d = new Dictionary<string, int>
{
    {"hello", 0},
    {"the answer to life the universe and everything is:", 42}
};

该功能的伟大之处在于它适用于带有任意数量参数的添加方法。例如,给定这个集合:

class c1 : IEnumerable
{
    void Add(int x1, int x2, int x3)
    {
        //...
    }

    //...
}

可以像这样初始化它:

var x = new c1
{
    {1,2,3},
    {4,5,6}
}

如果您需要创建复杂对象的静态表,这将非常有用。例如,如果您只是在使用List<Customer>并且想要创建客户对象的静态列表,则必须像这样创建它:

var x = new List<Customer>
{
    new Customer("Scott Wisniewski", "555-555-5555", "Seattle", "WA"),
    new Customer("John Doe", "555-555-1234", "Los Angeles", "CA"),
    new Customer("Michael Scott", "555-555-8769", "Scranton PA"),
    new Customer("Ali G", "", "Staines", "UK")
}

但是,如果您使用自定义集合,例如这个:

class CustomerList  : List<Customer>
{
    public void Add(string name, string phoneNumber, string city, string stateOrCountry)
    {
        Add(new Customer(name, phoneNumber, city, stateOrCounter));
    }
}

然后,您可以使用以下语法初始化集合:

var customers = new CustomerList
{
    {"Scott Wisniewski", "555-555-5555", "Seattle", "WA"},
    {"John Doe", "555-555-1234", "Los Angeles", "CA"},
    {"Michael Scott", "555-555-8769", "Scranton PA"},
    {"Ali G", "", "Staines", "UK"}
}

这样做的好处是更容易输入和阅读,因为它们不需要为每个元素重新输入元素类型名称。如果元素类型很长或很复杂,则优势可能特别强。

话虽如此,这仅在您需要在应用程序中定义的静态数据集合时才有用。某些类型的应用程序,例如编译器,一直在使用它们。其他的,例如典型的数据库应用程序,则不会,因为它们从数据库中加载所有数据。

我的建议是,如果您需要定义对象的静态集合,或者需要封装集合接口,则创建一个自定义集合类。否则我会直接使用List<T>

于 2008-08-22T04:46:28.433 回答
14

建议在公共 API 中不要使用 List<T>,而是使用 Collection<T>

如果你继承它,你应该没问题,afaik。

于 2008-08-22T03:39:05.990 回答
9

我更喜欢只使用List<BusinessObject>. 对它进行类型定义只是在代码中添加了不必要的样板。 List<BusinessObject>是一个特定的类型,它不仅仅是任何List对象,所以它仍然是强类型的。

更重要的是,声明一些东西List<BusinessObject>让每个阅读代码的人都更容易知道他们正在处理什么类型,他们不必搜索找出 aBusinessObjectCollection是什么然后记住它只是一个列表。通过类型定义,您必须要求每个人都必须遵循的一致(重新)命名约定才能使其有意义。

于 2008-08-22T03:34:18.840 回答
4

我一直在来回选择2个选项:

public class BusinessObjectCollection : List<BusinessObject> {}

或仅执行以下操作的方法:

public IEnumerable<BusinessObject> GetBusinessObjects();

第一种方法的好处是您可以更改底层数据存储,而不必弄乱方法签名。不幸的是,如果您从一个从先前实现中删除方法的集合类型继承,那么您将不得不在整个代码中处理这些情况。

于 2008-08-22T03:31:15.150 回答
4

使用List<BusinessObject>必须声明它们列表的类型。但是,如果您返回 列表BusinessObject,请考虑返回IEnumerable<T>IList<T>或者ReadOnlyCollection<T>- 即返回满足客户的最弱的可能合同。

您想在列表中“添加自定义代码”的地方,列表类型上的代码扩展方法。再次,将这些方法附加到可能最弱的合约,例如

public static int SomeCount(this IEnumerable<BusinessObject> someList)

当然,您不能也不应该使用扩展方法添加状态,因此如果您需要在其后面添加新属性和字段,请使用子类或更好的包装类来存储它。

于 2009-01-27T14:28:20.337 回答
3

您可能应该避免为此目的创建自己的集合。在重构期间或添加新功能时,想要多次更改数据结构的类型是很常见的。使用您的方法,您最终会为 BusinessObjectList、BusinessObjectDictionary、BusinessObjectTree 等创建一个单独的类。

仅仅因为类名更具可读性,我并没有真正看到创建此类的任何价值。是的,尖括号语法有点难看,但它在 C++、C# 和 Java 中是标准的,所以即使你不编写使用它的代码,你也会一直遇到它。

于 2008-08-22T03:35:09.090 回答
2

如果我需要“增加价值”,我通常只派生我自己的集合类。就像,如果集合本身需要一些“元数据”属性标记。

于 2008-08-22T03:28:35.267 回答
2

我和乔纳森做的事情完全一样……只是继承自List<T>. 你得到两全其美。但我通常只在需要添加一些价值时才这样做,比如添加LoadAll()方法或其他什么。

于 2008-08-22T03:31:48.597 回答
2

您可以同时使用两者。对于懒惰 - 我的意思是生产力 - List 是一个非常有用的类,它也是“全面的”并且坦率地说充满了 YANGNI 成员。再加上MSDN 文章已经链接的关于将 List 公开为公共成员的明智论点/建议,我更喜欢“第三种”方式:

就我个人而言,我使用装饰器模式仅从 List 中公开我需要的内容,即:

public OrderItemCollection : IEnumerable<OrderItem> 
{
    private readonly List<OrderItem> _orderItems = new List<OrderItem>();

    void Add(OrderItem item)
    {
         _orderItems.Add(item)
    }

    //implement only the list members, which are required from your domain. 
    //ie. sum items, calculate weight etc...

    private IEnumerator<string> Enumerator() {
        return _orderItems.GetEnumerator();
    }

    public IEnumerator<string> GetEnumerator() {
        return Enumerator();
    }    
}

此外,我可能会将 OrderItemCollection 抽象到 IOrderItemCollection 中,这样我就可以在将来交换 IOrderItemCollection 的实现(我可能更喜欢使用不同的内部可枚举对象,例如 Collection 或更喜欢使用键值对集合或 Set .

于 2009-01-27T14:07:59.253 回答
1

我对几乎所有场景都使用通用列表。我唯一会考虑使用派生集合的情况是添加集合特定成员。然而,LINQ 的出现已经减少了这种需求。

于 2008-08-22T05:02:08.037 回答
0

1 个中的 6 个,另一个半打

无论哪种方式都是一样的。只有当我有理由将自定义代码添加到 BusinessObjectCollection 中时,我才会这样做。

如果没有它的加载方法返回一个列表,我可以在一个通用的泛型类中编写更多代码并让它正常工作。比如Load方法。

于 2008-08-22T03:33:17.253 回答
0

正如其他人指出的那样,建议不要公开暴露 List,如果这样做,FxCop 会发牢骚。这包括从 List 继承,如下所示:

public MyTypeCollection : List<MyType>

在大多数情况下,公共 API 将适当地公开 IList(或 ICollection 或 IEnumerable)。

如果您想要自己的自定义集合,您可以通过从 Collection 继承而不是 List 来保持 FxCop 安静。

于 2008-09-16T18:15:57.470 回答
0

如果您选择创建自己的集合类,您应该检查System.Collections.ObjectModel 命名空间中的类型。

命名空间定义了基类,以使实现者更容易创建自定义集合。

于 2008-09-18T16:05:46.453 回答
0

如果我想屏蔽对实际列表的访问,我倾向于用我自己的集合来做。当您编写业务对象时,您可能需要一个钩子来了解您的对象是否正在被添加/删除,从这个意义上说,我认为 BOCollection 是更好的主意。因为如果不需要,List 更轻量级。如果您需要某种代理,您可能还需要检查使用 IList 以提供额外的抽象接口(例如,假集合触发数据库的延迟加载)

但是……为什么不考虑 Castle ActiveRecord 或任何其他成熟的 ORM 框架呢?:)

于 2008-09-18T16:11:12.743 回答
0

在大多数情况下,我只是简单地使用 List 方式,因为它在 90% 的时间里为我提供了我需要的所有功能,当需要“额外”的东西时,我从它继承,并编写额外的代码.

于 2008-09-18T16:13:08.933 回答
0

我会这样做:

using BusinessObjectCollection = List<BusinessObject>;

这只是创建一个别名而不是一个全新的类型。与直接使用 List<BusinessObject> 相比,我更喜欢它,因为它让我可以在未来的某个时候自由更改集合的底层结构,而无需更改使用它的代码(只要我提供相同的属性和方法)。

于 2008-10-10T16:07:09.983 回答
0

试试这个:

System.Collections.ObjectModel.Collection<BusinessObject>

不需要实现像 CollectionBase 这样的基本方法

于 2008-12-27T09:45:11.740 回答
-1

这是这样的:

返回数组,接受IEnumerable<T>

=)

于 2008-09-04T03:20:19.410 回答