创建封装Generic Collection的类有缺点吗?

时间:2010-10-15 08:07:09

标签: c# .net generics oop collections

我的(C#3.0 .NET 3.5)应用程序的一部分需要维护几个字符串列表。我毫不奇怪地将它们声明为List<string>并且一切正常,这很好。

这些List中的字符串实际上(并且始终)是基金ID。我想知道是否可能更明确地表达意图,例如:

public class FundIdList : List<string> { }

......这也有效。从技术上还是哲学上来说,这有什么明显的缺点吗?

5 个答案:

答案 0 :(得分:7)

我将从另一个方向开始:将字符串包装到名为FundId的类/结构中。我认为,这样做的优势大于通用列表与专业列表。

  1. 您的代码变得类型安全:将表示其他内容的字符串传递给需要基金标识符的方法的范围要小得多。
  2. 您可以将构造函数中有效的字符串约束为FundId,即强制执行最大长度,检查代码是否为预期格式,&amp; c。
  3. 您可以添加与该类型相关的方法/功能。例如,如果以“我”开头的基金代码是内部基金,您可以添加一个名为IsInternal的属性来形式化。
  4. 至于FundIdList,拥有这样一个类的优势类似于上面的FundId第3点:你有一个地方可以挂钩在FundId列表上运行的方法/函数(即聚合函数)。如果没有这样的地方,你会发现静态辅助方法开始在整个代码中出现,或者在一些静态辅助类中。

答案 1 :(得分:3)

列表与LT;&GT;没有虚拟或受保护的成员 - 这些类几乎从不被子类化。此外,虽然你可能需要List<string>的全部功能,但是如果你这样做的话 - 是否有必要制作这样的子类?

子类化有各种缺点。如果您将本地类型声明为FundIdList,那么您将无法通过例如使用linq和.ToList,因为您的类型更具体。我看到人们决定在这些列表中需要额外的功能,然后将其添加到子类列表类中。这是有问题的,因为List实现会忽略这些额外的位并可能违反您的约束 - 例如如果您要求唯一性并声明一个新的Add方法,那么通过将列表作为参数输入简单(合法地)转发到List<string>的任何人都将使用默认列表Add,而不是新的Add。您只能添加功能,永远不会删除它 - 并且没有需要子类化的受保护或虚拟成员才能利用。

因此,您无法使用扩展方法真正添加任何功能,并且您的类型不再完全兼容,这限制了您可以对列表执行的操作。

我更喜欢声明一个包含字符串的结构FundId,并实现有关该字符串所需的任何保证,然后使用List<FundId>而不是List<string>

最后,你的意思是List<>吗?我看到很多人使用List<>来表示IEnumerable<>或普通数组更适合的事情。在api中公开您的内部List特别棘手,因为这意味着任何API用户都可以添加/删除/更改项目。即使您首先复制列表,这样的返回值仍然会产生误导,因为人们可能期望能够添加/删除/更改项目。如果你在API中公开List而仅仅使用它进行内部簿记,那么声明和使用不添加任何功能的类型并不是那么有趣,只有文档

结论

仅对内部使用List<>,如果这样做,则不要将其子类化。如果你想要一些显式的类型安全,请在结构(而不​​是类)中包装string,因为结构在这里更有效并且具有更好的语义:空FundId和空字符串之间没有混淆,对象相等性和哈希码与结构一样工作,但需要为类手动指定)。最后,如果您需要支持枚举,或者如果您还需要索引,请在您的列表周围使用简单的ReadOnlyCollection<>包装器而不是让API客户端使用内部位来公开IEnumerable<>。如果确实需要可变列表API,ObservableCollection<>至少可以让您对客户端所做的更改做出反应。

答案 2 :(得分:2)

我个人会将其保留为List<string>,或者可能创建一个包裹字符串然后存储FundId的{​​{1}}类。

List<FundId>选项会强制执行类型更正,并允许您对List<FundId>进行一些验证。

答案 3 :(得分:1)

将其保留为List<string>,您的变量名称足以告诉其他人它存储的是FundID。

var fundIDList = new List<string>();

我何时需要继承List<T>

如果您对基金ID列表有特殊的操作/操作,请继承它。

public class FundIdList : List<string> 
{
   public void SpecialAction()
   {
      //can only do with a fund id list
      //sorry I can't give an example :(
   }
}

答案 4 :(得分:0)

除非我希望某人尽可能做List<string>所做的一切,而FundIdList没有任何干预,我宁愿实施IList<string>(或更高的界面)层次结构,如果我不关心该接口的大多数成员)并在适当时委托调用私有List<string>

如果我确实希望某人拥有这种程度的控制权,我可能会首先给他们一个List<string>。大概你有一些东西可以确保这些字符串实际上是“基金ID”,当你公开使用继承时,你不能再保证这些。

实际上,这听起来(通常与List<T>一样)就像私有继承的自然情况一样。唉,C#没有私有继承,所以组合是要走的路。