列表<BusinessObject> 还是 BusinessObjectCollection?
-
09-06-2019 - |
题
在 C# 泛型出现之前,每个人都会通过创建实现 IEnumerable 的集合基来为其业务对象编写集合代码
IE:
public class CollectionBase : IEnumerable
然后从中派生出他们的业务对象集合。
public class BusinessObjectCollection : CollectionBase
现在有了通用列表类,有人直接使用它吗?我发现我使用了两种技术的折衷方案:
public class BusinessObjectCollection : List<BusinessObject>
我这样做是因为我喜欢使用强类型名称,而不是仅仅传递列表。
什么是 你的 方法?
解决方案
我通常倾向于直接使用列表,除非由于某种原因我需要封装数据结构并提供其功能的有限子集。这主要是因为如果我没有特定的封装需求那么这样做只是浪费时间。
然而,随着 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>
直接地。
其他提示
它是 受到推崇的 在公共 API 中不要使用 List<T>,而是使用 Collection<T>
如果你继承它,你应该没问题,afaik。
我更喜欢只使用 List<BusinessObject>
. 。类型定义只是在代码中添加了不必要的样板。 List<BusinessObject>
是一个特定的类型,它不仅仅是任何类型 List
对象,所以它仍然是强类型的。
更重要的是,声明一些东西 List<BusinessObject>
让每个阅读代码的人都更容易知道他们正在处理什么类型,他们不必通过搜索来找出什么是 BusinessObjectCollection
是,然后记住这只是一个列表。通过类型定义,您必须要求每个人都必须遵循一致的(重新)命名约定才能使其有意义。
使用类型 List<BusinessObject>
您必须在其中声明它们的列表。然而
返回一个 BusinessObject
, ,考虑返回 IEnumerable<T>
, IList<T>
或者 ReadOnlyCollection<T>
- IE。返回令客户满意的最弱的合约。
如果您想要“添加自定义代码”到列表,请在列表类型上编写代码扩展方法。再次,将这些方法附加到最弱的可能合约上,例如
public static int SomeCount(this IEnumerable<BusinessObject> someList)
当然,您不能也不应该使用扩展方法添加状态,因此如果您需要添加新属性及其后面的字段,请使用子类或更好的包装类来存储它。
我一直在考虑两个选择:
public class BusinessObjectCollection : List<BusinessObject> {}
或只执行以下操作的方法:
public IEnumerable<BusinessObject> GetBusinessObjects();
第一种方法的好处是您可以更改底层数据存储,而不必弄乱方法签名。不幸的是,如果您继承的集合类型从先前的实现中删除了方法,那么您将必须在整个代码中处理这些情况。
您可能应该避免为此目的创建自己的集合。在重构或添加新功能时想要多次更改数据结构的类型是很常见的。通过您的方法,您最终会得到一个单独的 BusinessObjectList、BusinessObjectDictionary、BusinessObjectTree 等类。
我并没有真正看到创建此类的任何价值,只是因为类名更具可读性。是的,尖括号语法有点难看,但它是 C++、C# 和 Java 中的标准,因此即使您不编写使用它的代码,您也会一直遇到它。
我通常只在需要“增值”时派生自己的集合类。例如,如果集合本身需要一些“元数据”属性标记。
我和你做的事情一模一样,乔纳森......只是继承自 List<T>
. 。您将获得两全其美的效果。但我通常只在需要添加一些价值时才这样做,例如添加 LoadAll()
方法或者什么。
您可以同时使用两者。对于懒惰 - 我的意思是生产力 - List 是一个非常有用的类,它也是“全面的”并且坦率地说充满了 YANGNI 成员。加上提出的合理论点/建议 MSDN 文章 已经链接了有关将 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 。
我对几乎所有场景都使用通用列表。我唯一会考虑使用派生集合的情况是添加集合特定成员。然而,LINQ 的出现甚至减少了这种需求。
1 个中的 6 个,另外 6 个
无论哪种方式都是一样的。仅当我有理由将自定义代码添加到 BusinessObjectCollection 中时,我才会这样做。
如果没有它,加载方法会返回一个列表,这样我就可以在通用泛型类中编写更多代码并使其正常工作。比如Load方法。
正如其他人指出的那样,建议不要公开暴露 List,如果这样做,FxCop 会抱怨的。这包括从 List 继承,如下所示:
public MyTypeCollection : List<MyType>
在大多数情况下,公共 API 将根据需要公开 IList(或 ICollection 或 IEnumerable)。
如果您想要自己的自定义集合,可以通过继承 Collection 而不是 List 来保持 FxCop 安静。
如果您选择创建自己的集合类,您应该检查以下类型 System.Collections.ObjectModel 命名空间.
命名空间定义了基类,这些基类使实现者更容易创建自定义集合。
如果我想屏蔽对实际列表的访问,我倾向于使用我自己的集合来完成此操作。当您编写业务对象时,您可能需要一个钩子来知道您的对象是否被添加/删除,从这个意义上说,我认为 BOCollection 是更好的主意。当然,如果不需要的话,List 更轻量。另外,如果您需要某种代理(例如,假集合会触发数据库的延迟加载)
但...为什么不考虑 Castle ActiveRecord 或任何其他成熟的 ORM 框架?:)
大多数时候,我只是使用 List 方式,因为它在 90% 的时间里为我提供了我需要的所有功能,当需要“额外”的东西时,我继承它,并编写额外的位。
我会这样做:
using BusinessObjectCollection = List<BusinessObject>;
这只是创建一个别名而不是一个全新的类型。与直接使用 List<BusinessObject> 相比,我更喜欢它,因为它使我可以在将来的某个时刻自由地更改集合的底层结构,而无需更改使用它的代码(只要我提供相同的属性和方法)。
试试这个:
System.Collections.ObjectModel.Collection<BusinessObject>
不需要像 CollectionBase 那样实现基本方法
就是这样:
返回数组,接受 IEnumerable<T>
=)