C# 创建封装泛型集合的类是否有缺点?

C# 创建封装泛型集合的类是否有缺点?,c#,.net,generics,oop,collections,C#,.net,Generics,Oop,Collections,我的(C#3.0.NET3.5)应用程序的一部分需要维护多个字符串列表。毫不奇怪,我将它们声明为List,一切都正常,这很好 这些列表中的字符串实际上(并且总是)是基金ID。我想知道更明确是否更能揭示意图,例如: public class FundIdList : List<string> { } public-class-FundIdList:List{} 。。。这同样有效。在技术上或哲学上,这有什么明显的缺点吗 我个人会把它作为列表,或者可能创建一个FundId类来包装一个字

我的(C#3.0.NET3.5)应用程序的一部分需要维护多个字符串列表。毫不奇怪,我将它们声明为
List
,一切都正常,这很好

这些
列表中的字符串实际上(并且总是)是基金ID。我想知道更明确是否更能揭示意图,例如:

public class FundIdList : List<string> { }
public-class-FundIdList:List{}

。。。这同样有效。在技术上或哲学上,这有什么明显的缺点吗

我个人会把它作为
列表
,或者可能创建一个
FundId
类来包装一个字符串,然后存储一个
列表


List
选项将强制执行类型正确性,并允许您对
FundId

进行一些验证。我将从另一个方向开始:将字符串包装到名为FundId的类/结构中。我认为,这样做的好处大于通用列表和专用列表

  • 代码变得类型安全:将表示其他内容的字符串传递到需要基金标识符的方法的范围要小得多
  • 您可以将构造函数中有效的字符串约束为FundId,即强制执行最大长度,检查代码是否为预期格式,&c
  • 您可以添加与该类型相关的方法/函数。例如,如果以“I”开头的基金代码是内部基金,则可以添加一个名为IsInternal的属性,使其正式化
  • 对于FundIdList,拥有这样一个类的好处与上面关于FundId的第3点类似:您可以在FundId列表上挂接操作的方法/函数(即聚合函数)。如果没有这样的位置,您会发现静态助手方法开始在整个代码中出现,或者在某些静态助手类中出现。

    列表没有虚拟或受保护的成员-此类类几乎不应该被子类化。另外,尽管您可能需要
    列表的全部功能,但如果您需要,那么创建这样一个子类有多大意义呢

    子类化有很多缺点。如果您将本地类型声明为
    FundIdList
    ,则您将无法通过使用linq和
    .ToList
    等方式对其进行分配,因为您的类型更为具体。我看到人们决定在这样的列表中需要额外的功能,然后将其添加到子类列表类中。这是有问题的,因为列表实现忽略了这些额外的位,并且可能会违反您的约束-例如,如果您要求唯一性并声明一个新的添加方法,任何简单(合法)向上转换到
    List
    的人(例如,通过将列表作为这样键入的参数传递)都将使用默认的列表添加,而不是您的新添加。您只能添加功能,而不能删除它-并且没有需要子类化才能利用的受保护或虚拟成员

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

    我更喜欢声明一个包含字符串的结构
    FundId
    ,并在那里实现与该字符串相关的任何保证,然后使用
    列表
    而不是
    列表

    最后,你真的是说
    List
    我看到很多人使用
    List
    来处理
    IEnumerable
    或普通数组更适合的事情。在api中公开内部
    列表
    尤其棘手,因为这意味着任何api用户都可以添加/删除/更改项。即使您先复制列表,这样的返回值仍然会产生误导,因为人们可能希望能够添加/删除/更改项目。如果您不是在API中公开
    列表
    ,而只是将其用于内部簿记,那么声明和使用一个只添加文档而不添加任何功能的类型就没有那么有趣了

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

    只需将其作为
    列表
    ,您的变量名就足以告诉其他人它正在存储FundID

    var fundIDList = new List<string>();
    
    var fundIDList=new List();
    
    我何时需要继承
    列表

    如果要对基金id列表执行真正的特殊操作,请继承它

    public class FundIdList : List<string> 
    {
       public void SpecialAction()
       {
          //can only do with a fund id list
          //sorry I can't give an example :(
       }
    }
    
    公共类基金列表:列表
    {
    公共空间专题()
    {
    //只能使用基金id列表
    //对不起,我不能举个例子:(
    }
    }
    
    除非我希望有人尽其所能列出
    列表
    ,而不需要
    FundIdList
    的任何干预,否则我更愿意实现
    IList
    (或者,如果我不关心该接口的大多数成员,则实现层次结构更高的接口)并在适当的时候将调用委托给私有
    列表

    如果我真的希望有人拥有这种程度的控制权,我可能会首先给他们一个
    列表
    。大概你有办法确保这些字符串实际上是“基金ID”,当你公开使用继承时,你不能再保证这一点了