设计一个类,如果使用IList作为属性类型有什么问题?

时间:2015-07-08 05:00:08

标签: c# .net oop design-patterns

我在下面有一个简单的类,它有一个IList类型的属性Pages

有一些选项可以实现此属性,可以是ArrayCollection / List /偶ReadOnlyCollection

public class Book
    {
        private string[] _pages;

        public Book(string[] pages)
        {
            _pages = pages;
        }

        public IList<string> Pages
        {
            get
            {
                return _pages;
                //return new Collection<string>(_pages);
                //return new List<string>(_pages);
                //return new ReadOnlyCollection<string>(_pages);
            }
        }
    }

在设计时,我不知道客户将使用该属性的哪些操作,但选择上述任何选项都会影响其客户。

如果客户使用Book类,如下所示

var book = new Book(new[] {"A", "B"});
var pages = book.Pages;
pages[0] = "A2";

并非所有属性Pages的实现选项都适用于客户端。

选项1:为页面// OK, it works

返回一个数组
public IList<string> Pages
    {
        get
        {
            return _pages;
        }
    }

选项2:返回页面// KO, it throws an exception NotSupportedException Collection is read-only

的集合
public IList<string> Pages
    {
        get
        {
            return new Collection<string>(_pages);
        }
    }

选项3:返回页面列表// OK, it works

public IList<string> Pages
    {
        get
        {
            return new List<string>(_pages);
        }
    }

选项4:返回页面// KO, it throws an exception SystemException Collection is read-only

的ReadOnlyCollection
public IList<string> Pages
    {
        get
        {
            return new ReadOnlyCollection<string>(_pages);
        }
    }

我不认为客户端可能出错。任何人都可以给出一些解释并建议Page的好类型吗?

3 个答案:

答案 0 :(得分:3)

另一种选择是定义您自己的集合类型PageCollection,并在Book类中使用它。这样做的好处是,您可以隐藏集合实现方式的详细信息。这种方法的另一个优点是,您可以提供特殊的&#34;收集方法。

例如,你可以扩展现有的集合(它也可能是为Page创建自定义类的好建议,而不是使用普通字符串:

public class PageCollection : List<Page>
{
    // additional methods
}

您还可以包装现有的集合。这样做的好处是,您可以完全控制要为&#34;用户提供的方法。

public class PageCollection : IEnumerable<Page>
{
    private List<Page> _innerCollection = new List<Page>();

    public void RipOut(IEnumerable<Page> pages)
    {
        foreach (var page in pages)
        {
            _innerCollection.Remove(page);
        }
    }

    // other methods

    public IEnumerator<Page> GetEnumerator()
    {
        return _innerCollection.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

答案 1 :(得分:1)

如果我理解你的问题,你会问:如何在不知道如何使用课程的情况下设计课程?

答案是,通过添加您想要支持的功能来设计它。

在您的示例中: 你有一本书页。您是否希望用户能够转到第0页,将其删除,并将其替换为其他内容?如果是这样,请将其设为列表。如果没有,请将其设为ReadOnlyCollection。

我建议将属性公开为实际属性(List或ReadOnlyCollection),这样就可以明确表达允许的内容,并且没有客户端猜测它们是否可以替换页面。

答案 2 :(得分:1)

所以最后我来了这个设计。自定义PageCollection具有添加/删除方法将允许我在里面添加验证行为。如果使用List<Page>,则无法做到这一点。

本书现在公开IPageCollection页面,其方法少于IList。

更新:以下是创建新界面的原因:

  • IList / List在这种情况下是不一致的,因为它有点胖,就像有人说的那样。
  • 列表不允许在列表中添加/删除页面时进行自定义验证
  • 使用通用列表不满足a Code Analysis rule
  • 如果需要其他方法,如AddRange,Clear ...它易于实现

public class Page
{
}

public interface IPageCollection : IReadOnlyList<Page>
{
    void Add(Page page);
    void Remove(Page page);
}

public class PageCollection : IPageCollection
{
    public PageCollection(IList<Page> pages)
        : base(pages)
    {
    }

    public void Add(Page page)
    {
    }

    public void Remove(Page page)
    {
    }

    ...
}

public class Book
{
    private readonly PageCollection _pages;

    public Book(IList<Page> pages)
    {
        _pages = new PageCollection(pages);
    }

    public IPageCollection Pages
    {
        get { return _pages; }
    }
}

(感谢约翰,托马斯......的建议)