【问题标题】:Custom Collection vs Generic Collection for public methods [closed]公共方法的自定义集合与通用集合[关闭]
【发布时间】:2010-12-05 09:54:48
【问题描述】:

公开自定义集合与通用集合的框架设计指南是什么? 例如

public class ImageCollection : Collection<Image>
{
     ...
}

public class Product
{
   public ImageCollection {get; set;}
}

VS

public class Product
{
   public Collection<Image> Images{get; set;}
}

【问题讨论】:

  • 第三种可能性是公开 IList of Image。
  • 当我说“收藏”时,我统称为 IList、List 等。我认为这个问题仍然适用,即使它适用于 List 或 IList
  • .NET fx 指南很快指出,还有一个可读性方面,在许多情况下,自定义集合优于通用列表
  • Wololo,好吧,暴露一个具体的类和一个接口是有很大区别的。
  • JB Evain,我确定有区别,但两者之间我并不完全清楚。愿意分享更多细节吗?

标签: c# .net collections api-design


【解决方案1】:

我会选择第二个。无论如何,客户端代码都必须知道 Image 类型。

第一个只是意味着更多的代码。

【讨论】:

    【解决方案2】:

    使用Collection&lt;T&gt; 的唯一原因是,如果您想挂接到一组虚拟方法中,这样您就可以查看底层列表的所有突变。这允许您执行诸如响应删除、实现事件等操作......

    如果您对响应或观看这些集合修改不感兴趣,那么List&lt;T&gt; 是更合适的类型。

    【讨论】:

      【解决方案3】:

      如果您要构建公共 API,请考虑使用自定义集合以获得以下优势。

      • 可扩展性 - 将来您可以在不更改 API 的情况下向自定义集合添加功能。

      • 向后兼容性 - 您可以随意更改集合类的内部结构,而不会破坏客户针对集合编写的现有代码。

      • 还可以更轻松地进行潜在的重大更改,同时减少影响。也许稍后您决定不希望它是 Collection&lt;Image&gt;,而是不同的集合类型 - 您可以定制 ImageCollection 类以具有与以前相同的方法,但从不同的东西继承 - 客户端代码不会可能会破裂。

      如果只是用于内部使用代码,则该决定不一定那么重要,您可能会选择“越简单越好”。

      我想到的一个例子是 ESRI 的 ArcGIS Server API - 他们在其 API 中使用自定义集合。我们也在 API 中使用自定义集合,主要是出于这些原因。

      【讨论】:

      • 实际上,更改类的基本类型被认为是一项重大更改,(例如,如果客户端代码将其分配给Collection&lt;Image&gt;)但通常无关紧要。
      • 我绝不会推荐将任何 ArcGIS API 作为“好”.NET 的示例来遵循。他们使用自定义集合,因为他们坚持使用包装 COM - 但他们也在 .NET api 中的任何地方使用 java 风格的迭代器,不要在他们应该的地方公开 IEnumerable 或 IEnumerable 以及许多其他让生活变得非常困难。
      • @Slaks - 非常正确。无论如何,它减轻了很多。
      • @Reed - 注意我没有在任何地方说“好”,哈哈。这是我想到的第一个,因为我必须经常使用 WebADF。
      • @womp:我永远不会列出某些东西,例如,(IMO)作为 API 的可怕之处。仅 ESRI 使用自定义集合这一事实就让我认为这是一个糟糕的选择! (就个人而言,这不是我最喜欢的选择 - 请参阅我的回答,了解为什么,但 Arc*** API 仍然很糟糕,很糟糕,很糟糕。)
      【解决方案4】:

      我能想到的主要原因是,您可能希望有一些特定于图像集合的方法,这些方法可以在该集合本身上运行。然后我会使用 ImageCollection 类来包含这些方法。经典封装。

      另外,顺便说一句,您实际上应该只为该集合公开一个 getter,并使用实际集合初始化一个只读支持字段。您可能希望人们在集合中添加/删除项目,而不是替换整个项目:

      class Product {
          private readonly Collection<Image> _images;
          public Product() { _images = new Collection<Image>(); }
          public Collection<Image> Images { get { return _images; } }
      }
      

      -奥辛

      【讨论】:

      • +1 获取 getter 的小费。
      【解决方案5】:

      一般来说,最好公开其中一个接口,例如IEnumerable&lt;T&gt;ICollection&lt;T&gt;IList&lt;T&gt;,而不是具体的类。

      这为您在更改内部 API 方面提供了更大的灵活性。尤其是IEnumerable&lt;T&gt;,它允许您稍后修改内部结构以允许结果流式传输,因此是最灵活的。

      如果您知道“集合”的预期使用模式,您应该公开适当的 API,以便将来对您提供最少的限制。例如,如果您知道人们只需要迭代您的结果,公开IEnumerable&lt;T&gt;,这样您就可以稍后更改为任何集合,甚至直接切换为只使用yield return。

      【讨论】:

      • 我想我同意你的观点,对内部 api 使用最简单的“集合”。但是您会在公共 Api 中公开 IEnumerable、ICollection 或 IList 吗?或者您会创建一个实现 IEnumerable、ICollection 或 IList 的自定义“集合”,从而涵盖上述优点 womp?
      • 没有。框架设计指南书实际上(现在)建议在公共 API 中公开 IEnumerable、IList 等。它们为您提供了最大的灵活性,可以在不破坏公共 API 的情况下更改您的内部实现。如果您实现了自定义集合,您将永远被锁定为该类型,除非您想破坏 API 兼容性。
      【解决方案6】:

      我更喜欢有一个公共的通用属性来使内部可扩展,但私有字段将是具有实际功能的自定义字段。

      通常是这样的:

      public interface IProduct
      {
           ICollection<Image> Images { get; }
      }
      
      public class Product : IProduct
      {
         private class ImageCollection : Collection<Image>
         {
            // override InsertItem, RemoveItem, etc.
         }
      
         private readonly ImageCollection _images;
         public ICollection<Image> Images
         {
            get { return _images; }
         }
      }
      

      我还经常将自定义集合类嵌套在不同的类中,这允许我在添加/删除期间修改父类的一些私有成员。

      【讨论】:

        猜你喜欢
        • 2010-09-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-11-22
        • 1970-01-01
        • 1970-01-01
        • 2012-03-27
        • 2023-04-07
        相关资源
        最近更新 更多