[英]Are there drawbacks to creating a class that encapsulates Generic Collection?
A part of my (C# 3.0 .NET 3.5) application requires several lists of strings to be maintained. 我的(C#3.0 .NET 3.5)应用程序的一部分需要维护几个字符串列表。 I declare them, unsurprisingly, as
List<string>
and everything works, which is nice. 我毫不奇怪地将它们声明为
List<string>
并且一切正常,这很好。
The strings in these List
s are actually (and always) Fund IDs. 这些
List
的字符串实际上(并且始终)是基金ID。 I'm wondering if it might be more intention-revealing to be more explicit, eg: 我想知道更明确的意图是否更具意图,例如:
public class FundIdList : List<string> { }
... and this works as well. ......这也有效。 Are there any obvious drawbacks to this, either technically or philosophically?
从技术上还是哲学上来说,这有什么明显的缺点吗?
I would start by going in the other direction: wrapping the string up into a class/struct called FundId. 我将从另一个方向开始:将字符串包装到名为FundId的类/结构中。 The advantage of doing so, I think, is greater than the generic list versus specialised list.
我认为,这样做的优势大于通用列表与专用列表。
As for FundIdList, the advantage to having such a class is similar to point 3 above for the FundId: you have a place to hook in methods/functions that operate on the list of FundIds (ie aggregate functions). 至于FundIdList,拥有这样一个类的优势类似于FundId上面的第3点:你有一个地方可以挂钩在FundIds列表上运行的方法/函数(即聚合函数)。 Without such a place, you'll find that static helper methods start to crop up throughout the code or, in some static helper class.
如果没有这样的地方,你会发现静态帮助器方法开始在整个代码中出现,或者在一些静态助手类中出现。
List<> has no virtual or protected members - such classes should almost never be subclassed. List <>没有虚拟或受保护的成员 - 这些类几乎不应该是子类。 Also, although it's possible you need the full functionality of
List<string>
, if you do - is there much point to making such a subclass? 此外,虽然你可能需要
List<string>
的全部功能,但如果你这样做 - 是否有必要制作这样的子类?
Subclassing has a variety of downsides. 子类化有各种缺点。 If you declare your local type to be
FundIdList
, then you won't be able to assign to it by eg using linq and .ToList
since your type is more specific. 如果您将本地类型声明为
FundIdList
,那么您将无法通过例如使用linq和.ToList
来分配它,因为您的类型更具体。 I've seen people decide they need extra functionality in such lists, and then add it to the subclassed list class. 我看到人们决定在这些列表中需要额外的功能,然后将其添加到子类列表类中。 This is problematic, because the List implementation ignores such extra bits and may violate your constraints - eg if you demand uniqueness and declare a new Add method, anyone that simply (legally) upcasts to
List<string>
for instance by passing the list as a parameter typed as such will use the default list Add, not your new Add. 这是有问题的,因为List实现忽略了这些额外的位并且可能违反了你的约束 - 例如,如果你要求唯一性并声明一个新的Add方法,那么任何人(例如)通过将列表作为一个简单地(合法地)向上转换为
List<string>
任何人这样输入的参数将使用默认列表Add,而不是新的Add。 You can only add functionality, never remove it - and there are no protected or virtual members that require subclassing to exploit. 您只能添加功能,永远不会删除它 - 并且没有需要子类化来利用的受保护或虚拟成员。
So you can't really add any functionality you couldn't with an extension method, and your types aren't fully compatible anymore which limits what you can do with your list. 因此,您无法使用扩展方法添加任何功能,并且您的类型不再完全兼容,这限制了您可以对列表执行的操作。
I prefer declaring a struct FundId
containing a string and implementing whatever guarantees concerning that string you need there, and then working with a List<FundId>
rather than a List<string>
. 我更喜欢声明一个包含字符串的struct
FundId
,并实现有关该字符串所需的任何保证,然后使用List<FundId>
而不是List<string>
。
Finally, do you really mean List<>
? 最后,你真的是指
List<>
吗? I see many people use List<>
for things for which IEnumerable<>
or plain arrays are more suitable. 我看到很多人使用
List<>
来处理IEnumerable<>
或plain数组更适合的事情。 Exposing your internal List
in an api is particularly tricky since that means any API user can add/remove/change items. 在api中公开内部
List
特别棘手,因为这意味着任何API用户都可以添加/删除/更改项目。 Even if you copy your list first, such a return value is still misleading, since people might expect to be able to add/remove/change items. 即使您首先复制列表,这样的返回值仍然会产生误导,因为人们可能希望能够添加/删除/更改项目。 And if you're not exposing the
List
in an API but merely using it for internal bookkeeping, then it's not nearly as interesting to declare and use a type that adds no functionality, only documentation. 如果您没有在API中公开
List
,而只是将其用于内部簿记,那么声明和使用不添加任何功能的类型(仅文档)并不是那么有趣。
Only use List<>
for internals, and don't subclass it if you do. 仅对内部使用
List<>
,如果这样做,则不要将其子类化。 If you want some explicit type-safety, wrap string
in a struct (not a class, since a struct is more efficient here and has better semantics: there's no confusion between a null FundId
and a null string, and object equality and hashcode work as expected with structs but need to be manually specified for classes). 如果你想要一些显式的类型安全,请在结构中包装
string
(不是类,因为结构在这里更有效并且具有更好的语义:在null的FundId
和空字符串之间没有混淆,并且对象相等和哈希码工作为预期结构但需要手动指定类)。 Finally, expose IEnumerable<>
if you need to support enumeration, or if you need indexing as well use the simple ReadOnlyCollection<>
wrapper around your list rather than let the API client fiddle with internal bits. 最后,如果您需要支持枚举,或者如果您还需要索引,则使用简单的
ReadOnlyCollection<>
包装器来展示IEnumerable<>
,而不是让API客户端使用内部位。 If you really need a mutatable list API, ObservableCollection<>
at least lets you react to changes the client makes. 如果您确实需要可变列表API,
ObservableCollection<>
至少可以让您对客户端所做的更改做出反应。
Personally I would leave it as a List<string>
, or possibly create a FundId
class that wraps a string and then store a List<FundId>
. 我个人会把它
FundId
List<string>
,或者可能创建一个包装字符串然后存储List<FundId>
的FundId
类。
The List<FundId>
option would enforce type correct-ness and allow you to put some validation on FundIds
. List<FundId>
选项将强制执行类型更正,并允许您对FundIds
进行一些验证。
Just leave it as a List<string>
, you variable name is enough to tell others that it's storing FundIDs. 只需将其保留为
List<string>
,您的变量名称就足以告诉其他人它正在存储FundID。
var fundIDList = new List<string>();
When do I need to inherit List<T>
? 我什么时候需要继承
List<T>
?
Inherit it if you have really special actions/operations to do to a fund id 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 :(
}
}
Unless I was going to want someone to do everything they could to List<string>
, without any intervention on the part of FundIdList
I would prefer to implement IList<string>
(or an interface higher up the hierarchy if I didn't care about most of that interface's members) and delegate calls to a private List<string>
when appropriate. 除非我想要某人做他们可以做的所有事情来
List<string>
,而没有对FundIdList
进行任何干预,我宁愿实现IList<string>
(或者如果我不关心的话,可以实现层次结构更高层次的接口大多数接口的成员)并在适当时委托调用私有List<string>
。
And if I did want someone to have that degree of control, I'd probably just given them a List<string>
in the first place. 如果我确实希望有人拥有这种程度的控制权,我可能只是首先给他们一个
List<string>
。 Presumably you have something to make sure such strings actually are "Fund IDs", which you can't guarantee any more when you publicly use inheritance. 据推测,你有一些东西可以确保这些字符串实际上是“基金ID”,当你公开使用继承时,你不能再保证。
Actually, this sounds (and often does with List<T>
) like a natural case for private inheritance. 实际上,这听起来(并且经常使用
List<T>
)就像私有继承的自然情况一样。 Alas, C# doesn't have private inheritance, so composition is the way to go. 唉,C#没有私有继承,所以组合是要走的路。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.