【问题标题】:Using ReadOnlyCollection and a Getter使用 ReadOnlyCollection 和 Getter
【发布时间】:2016-03-08 19:07:03
【问题描述】:

昨天我问了一个关于深度克隆列表的问题,我得到了一个很好的答案,你可以阅读here

我遇到的问题是答案使用ImmutableList,我对此没有任何问题,只是如果我想使用 ReadOnlyCollection 并确保返回我收藏的副本并且类中的那个不能被修改。

我只是想知道以下是否正确。

    private ReadOnlyCollection<Author> listofAuthors;   
    private List<Author> copyofAuthors;

    public Book(ICollection<Author> authors)
    {

        copyofAuthors = new List<Author>(authors);
        listofAuthors = new ReadOnlyCollection<Author>(new List<Author>(copyofAuthors));

    }

    public ICollection<Author> Authors
    {
        get
        {
            return new ReadOnlyCollection<Author>(new List<Author>(copyofAuthors));
        }
    }

根据MSDN documentation ReadOnlyCollection 只是一个底层可变集合的包装器。因此,如果对基础集合进行任何更改,它将反映在 ReadOnlyCollection 中。上面的代码 getter 返回一个新的 List 作为只读集合。

问题一:

鉴于上述代码,任何其他调用它的代码都将获得 私有 ReadOnly(new List()) 的副本,对吗?用户所做的任何更改都不会反映在 Book 类中的 ReadOnlyCollection 中,对吧?

问题 2:

我知道ImmutableList 更理想,但是如果我需要使用ReadOnlyCollection&lt;Authors&gt;,我在构造函数/获取器中所做的是否正确?还是可以以另一种/更好的方式实现?

【问题讨论】:

    标签: c# deep-copy


    【解决方案1】:

    考虑以下类:

    public Book(ICollection<Author> origAuthors)
    {
        // We only need to allocate the ReadOnlyCollection once.
        this.Authors = new ReadOnlyCollection<Author>( new List<Author>( origAuthors ) );
    
    }
    
    public ICollection<Author> Authors { get; private set; }
    

    在这段代码中,origAuthors 存储了一组对Author 对象的引用。该集合可由提供它的任何人修改。我还假设Author 是一个类而不是一个结构,因此具有引用语义。

    此代码复制了对Author 对象的引用列表,用ReadOnlyCollection 实例包装该列表,并通过我们的Book.Authors 属性公开该ReadOnlyInstance 实例。

    由于Book.Authors 属性最终是指作者列表的副本,如果我们从origAuthors 中删除一个元素,那么Book.Authors 将不会反映它;它将保留以前的作者集。

    例如:

    void Test() {
        List<Author> mutableAuthors = new List<Author>();
        mutableAuthors.AddRange( ... add 5 authors ... );
    
        Book testBook = new Book( mutableAuthors );
        mutableAuthors.Clear();
    
        // Prints 5.
        Console.Writeline( testBook.Authors.Count );
    
        // Throws an exception from ReadOnlyCollection.
        testBook.Authors.Clear();
    }
    

    然而,到目前为止,我们讨论的只是一组引用 Author 对象的列表;我们还没有和 Author 对象本身谈过。它们是可变的吗?

    如果是这样,无论人们如何检索对 Author 的引用,他们都可能修改 Author 对象本身。使用ReadOnlyCollection&lt;Author&gt; 只会阻止您修改列表中的哪些项目;它不会阻止您自己修改项目。

    考虑以下代码:

    void Test() {
        List<Author> mutableAuthors = new List<Author>();
        mutableAuthors.AddRange( ... add 5 authors ... );
    
        Book testBook = new Book( mutableAuthors );
        mutableAuthors.Clear();
    
        // Prints 5.
        Console.Writeline( testBook.Authors.Count );
    
        Author someAuthor = testBook.Authors[0];
        someAuthor.ChangeName("A new name");
    
        // Prints "A new name".
        Console.WriteLine( testBook.Authors[0] );
    }
    

    如果您想防止这种情况发生,请确保您对 Author 类的设计是不可变的 - 一旦构建完成,就不要为消费者提供任何修改它的方法。

    【讨论】:

    • 是的,作者是不可变的,我应该说明这一点。
    • 只有一个问题,假设 Authors 是不可变的,我可以使用您指定的 ReadOnlyCollection,而不必担心用户可以更改内部(Book)集合,对吗?您的代码的第一部分似乎指定了哪个。
    • @Svetlana - 宾果游戏。由于您制作了列表的副本,因此当原始列表更改时,您的副本不会更改。然后您需要担心确保没有人修改您的列表副本;您可以通过 ReadOnlyList&lt;T&gt; 包装类公开它来做到这一点。最后,我们需要确保事物本身,不仅是事物列表,是不可变的。但正如您所说,Author 是不可变的。所以我们所有的基地都被覆盖了。
    • 我不建议公开具体类型(SOLID 原则),而是将ICollection 更改为IReadOnlyCollection
    • @ErikPhilips - “我不建议公开具体类型”。您在哪里看到过这样一种类型?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-15
    • 1970-01-01
    • 1970-01-01
    • 2017-12-23
    • 1970-01-01
    • 2019-11-26
    相关资源
    最近更新 更多