创建封装通用集合的类有缺点吗?

发布于 2024-09-27 06:25:03 字数 289 浏览 3 评论 0原文

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

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

public class FundIdList : List<string> { }

……这也有效。无论是在技术上还是在哲学上,这有什么明显的缺点吗?

A part of my (C# 3.0 .NET 3.5) application requires several lists of strings to be maintained. I declare them, unsurprisingly, as List<string> and everything works, which is nice.

The strings in these Lists are actually (and always) Fund IDs. I'm wondering if it might be more intention-revealing to be more explicit, e.g.:

public class FundIdList : List<string> { }

... and this works as well. Are there any obvious drawbacks to this, either technically or philosophically?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

浅紫色的梦幻 2024-10-04 06:25:03

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

  1. 您的代码变得类型安全:您将表示其他内容的字符串传递到需要基金标识符的方法的范围要小得多。
  2. 您可以将构造函数中有效的字符串限制为 FundId,即强制执行最大长度,检查代码是否采用预期格式 &c。
  3. 您可以添加与该类型相关的方法/函数。例如,如果以“I”开头的基金代码是内部基金,您可以添加一个名为 IsInternal 的属性来正式说明这一点。

至于 FundIdList,拥有这样一个类的优点类似于上面针对 FundId 的第 3 点:您可以挂钩对 FundId 列表进行操作的方法/函数(即聚合函数)。如果没有这样的地方,您会发现静态帮助器方法开始在整个代码中或在某些静态帮助器类中出现。

I would start by going in the other direction: wrapping the string up into a class/struct called FundId. The advantage of doing so, I think, is greater than the generic list versus specialised list.

  1. You code becomes type-safe: there is a lot less scope for you to pass a string representing something else into a method that expects a fund identifier.
  2. You can constrain the strings that are valid in the constructor to FundId, i.e. enforce a maximum length, check that the code is in the expected format, &c.
  3. You have a place to add methods/functions relating to that type. For example, if fund codes starting 'I' are internal funds you could add a property called IsInternal that formalises that.

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 (i.e. aggregate functions). Without such a place, you'll find that static helper methods start to crop up throughout the code or, in some static helper class.

吾性傲以野 2024-10-04 06:25:03

列表>>没有虚拟或受保护的成员 - 此类类几乎永远应该被子类化。另外,虽然您可能需要 List 的完整功能,但如果您这样做 - 创建这样的子类有什么意义吗?

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

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

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

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

结论

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

List<> has no virtual or protected members - such classes should almost never be subclassed. 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?

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 e.g. using linq and .ToList since your type is more specific. 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 - e.g. 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. 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>.

Finally, do you really mean List<>? I see many people use List<> for things for which IEnumerable<> or plain arrays are more suitable. Exposing your internal List in an api is particularly tricky since that means any API user can add/remove/change items. 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.

Conclusion

Only use List<> for internals, and don't subclass it if you do. 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). 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. If you really need a mutatable list API, ObservableCollection<> at least lets you react to changes the client makes.

野鹿林 2024-10-04 06:25:03

就我个人而言,我会将其保留为 List,或者可能创建一个 FundId 类来包装字符串,然后存储 List >。

List 选项将强制类型正确性,并允许您对 FundIds 进行一些验证。

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>.

The List<FundId> option would enforce type correct-ness and allow you to put some validation on FundIds.

-柠檬树下少年和吉他 2024-10-04 06:25:03

只需将其保留为 List,您的变量名称就足以告诉其他人它正在存储 FundID。

var fundIDList = new List<string>();

我什么时候需要继承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 :(
   }
}

Just leave it as a List<string>, you variable name is enough to tell others that it's storing FundIDs.

var fundIDList = new List<string>();

When do I need to inherit List<T>?

Inherit it if you have really special actions/operations to do to a fund id list.

public class FundIdList : List<string> 
{
   public void SpecialAction()
   {
      //can only do with a fund id list
      //sorry I can't give an example :(
   }
}
寄风 2024-10-04 06:25:03

除非我希望有人对 List 尽其所能,而无需 FundIdList 进行任何干预,否则我更愿意实现 IListstring>(或者层次结构中更高的接口,如果我不关心该接口的大多数成员),并在适当的时候将调用委托给私有List

如果我确实希望某人拥有这种程度的控制权,我可能首先会给他们一个 List 。想必您有办法确保这些字符串实际上是“基金 ID”,而当您公开使用继承时,您将无法再保证这一点。

实际上,这听起来(对于 List 来说经常如此)就像私有继承的自然情况。唉,C# 没有私有继承,所以组合是最佳选择。

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.

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. 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.

Actually, this sounds (and often does with List<T>) like a natural case for private inheritance. Alas, C# doesn't have private inheritance, so composition is the way to go.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文