【问题标题】:Class design: Access a List<T> directly or through methods?类设计:直接或通过方法访问 List<T>?
【发布时间】:2010-12-13 00:04:16
【问题描述】:

对于新闻编辑室系统,我有一个包含单个新闻故事的类。此类内部是一个私有变量,其中包含图像类的通用列表。一个故事的想法可以包含多个图像。

问题是我是否应该公开 List 变量,以便我可以通过直接寻址 List 来添加/删除图像

public class News
{
    private _images List<Images>();

    public Images
    {
      get { return _images; }
      set { _images = value }
    }
}

我是否应该将 List 变量设为私有,然后创建操作它的方法:

public class News
{
    private _images List<Images>();

    public void AddImage( Image image )
    public Image GetImage( int imageId )
    public int GetImageCount()
    public void DeleteImage( int imageId )
}

我的蜘蛛感觉告诉我要稍后做,因为它会更多地抽象事物。但另一方面,它会创建更多代码。

【问题讨论】:

    标签: c# .net class oop


    【解决方案1】:

    通过公开列表,您可以公开实现细节。从短期来看,这会让事情变得更容易,但如果你决定这样做,你以后会很难过。将列表更改为其他容器(也许您需要字典进行查找或其他内容)。

    我会封装它,因为它将使该类型在将来更易于维护和增强。

    【讨论】:

      【解决方案2】:

      这取决于您是否需要/(将需要)控制添加/获取和删除图像或更改容器的可能性。

      【讨论】:

        【解决方案3】:

        如果您将列表公开为属性,则可以从类外部执行以下操作:

        News.Images = new List<Images>();
        

        这就是你想要的吗? (你不应该,因为它违反了各种封装原则)

        如果没有,则使用 ICollection 接口:

        class News
        {
            public ICollection<Image> Images   
            {
                get;
                private set;
            }
         }
        

        class News
        {
            private List<Image> images = new List<Image>();
            public ICollection<Image> Images   
            {
                get
                {
                     // You can return an ICollection interface directly from a List
                     return images;
                }
            }
         }
        

        ICollection 有 Add、Remove、Clear、Count 等方法。

        如果你想要一个只读容器返回一个 ReadOnlyCollection

        class News
        {
            private List<Image> images = new List<Image>();
            public ReadOnlyCollection<Image> Images   
            {
                get
                {
                     // This wraps the list in a ReadOnlyCollection object so it doesn't actually copy the contents of the list just a reference to it
                     return images.AsReadOnly();
                }
            }
         }
        

        【讨论】:

          【解决方案4】:

          公开列表的只读实现并公开用于操作列表的方法。我会这样做:

          public class News
          {
              private IList<Image> _images;
          
              public void News()
              {
                  _images = new List<Image>();
              }
          
              public void AddImage(Image image) { ... }
              public void RemoveImage(Image image) { ... }
          
              public IEnumberable<Image> Images
              {
                  get { return _images; }
              }
          }
          

          请注意,可以将 Images 属性转换为 List,但如果需要,您可以在 ReadyOnlyCollection 包装器中返回它。 Count() 和 ElementAt() 扩展方法替换了您的 GetImageCount 和 GetImage 方法。

          【讨论】:

            【解决方案5】:

            对于访问元素,您可以考虑使用 ReadOnlyCollection 或 IEnumerable 类型。但是为了保证封装,你应该使用插入/删除方法,这样你就不再需要属性集了。

            编辑:在输入这​​个答案时有人打败了我;)

            【讨论】:

              【解决方案6】:

              我认为这是一个只有您可以决定的设计考虑因素。您的第二种方法是隐藏一个实现细节,即您使用List 来存储图像。另一方面,第一个解决方案为您提供了一个优势。您可以使用所有 List 方法,包括那些总是有用的扩展。使用第二种解决方案,您还可以实现一个ToList() 方法,该方法将返回一个新构造的List。对此List 的更改不会影响class 的内部结构。不利的一面是,如果内部图像List 太大,可能会影响性能,因为它总是会在ToList() 上构建一个新的List,但我不希望这个方法被多次调用。

              另一种解决方案是公开ReadOnlyCollection

              【讨论】:

                【解决方案7】:

                如果您直接公开列表,您将不得不依赖它的机制来执行所有与图像相关的操作(添加、删除、计数...)

                我仍然会公开一组图像(ReadOnlyCollection 通常很好)以使开发人员和消费者更容易访问列表的操作,但所有创建/更新/删除逻辑都应该包含在您的类中。

                【讨论】:

                  【解决方案8】:

                  我会将列表的只读视图公开为 IList 或 IEnumerable,以及添加和删除元素的方法。像这样:

                  public class News
                  {
                      private _images List<Images>();
                  
                      public IList<Image> Images
                      {
                          get {return _images.AsReadOnly(); }
                      }
                  
                      public void AddImage(Image image)
                      {
                          _images.Add(image);
                          // Do other stuff...
                      }
                  
                      public void DeleteImage(Image image)
                      {
                          _images.Remove(image);
                          // Do other stuff...
                      }
                  }
                  

                  【讨论】:

                  • 我猜 Images 属性在这里缺少 'get {' 部分?
                  • 使用 IList 会公开实现(即知道它是一个 List) - 有时这过于具体,值得使用更通用的类型,例如 ICollection。
                  • @Matt:请注意,我在顶部确实说过“作为 IList 或 IEnumerable”。当然 IEnumerable 足够通用。
                  【解决方案9】:

                  戴维·布里昂上周对此发表了一篇文章。他倾向于公开 IEnumerable 属性并提供用于操作的添加和删除方法。

                  大多数时候你只想循环遍历集合,所以 IEnumerable 可以解决问题。此外,您可以在需要时毫不费力地切换实际实现(List,Set,...),当切换到另一个 ORM 时,这可能非常有价值。

                  http://davybrion.com/blog/2009/10/stop-exposing-collections-already/

                  【讨论】:

                  • 通过公开 ICollection,您可以更改底层实现 - 不使用 ICollection 的唯一参数是如果您需要在集合内容更改时收到通知。
                  • ICollection 确实是一个有效的替代方案,这完全取决于您要对“集合”做什么。如果您只是要遍历它:IEnumerable,如果您想知道某个时候的大小:ICollection。在这个级别上这是一个品味问题,只要你不使用 IList 就可以了。
                  • 同意 - 这是一个了解每种方法的优缺点的案例。我似乎记得在某处使用 Linq .Count 只会返回列表的 .Count 而不是遍历所有项目。
                  【解决方案10】:

                  我只是将列表公开为 IList:

                  public class News
                  {
                      private List<Image> _images;
                  
                      public IList<Image> Images
                      {
                          get { return _images; }
                          set { _images = value; }
                      }
                  }
                  

                  如果您以后想更改实现,可以在不违反合同的情况下这样做:

                  public class News
                  {
                      public News(SomeCollection<Image> images)
                      {
                          _images = images;
                          Images = new ListView(this);
                      }
                  
                      private SomeCollection<Image> _images;
                  
                      public IList<Image> Images { get; private set; }
                  
                      private class News.ListView : IList<Image>
                      {
                          public ListView(News news)
                          {
                              _news = news;
                          }
                  
                          private News _news;
                  
                          // Implement the methods to manipulate _news._images
                      }
                  }
                  

                  【讨论】:

                    猜你喜欢
                    • 2018-07-30
                    • 2017-09-24
                    • 1970-01-01
                    • 2010-09-26
                    • 2010-11-22
                    • 2018-06-29
                    • 2011-03-13
                    • 2013-07-01
                    • 2011-01-09
                    相关资源
                    最近更新 更多