Best practice: How to expose a read-only ICollection

前端 未结 7 1580
礼貌的吻别
礼貌的吻别 2020-12-16 02:39

I have an ICollection called foos in my class which I want to expose as read-only (see this question). I see that the interface defines a

相关标签:
7条回答
  • 2020-12-16 02:49

    Since the question was written, .NET 4.0 has added an IReadOnlyCollection<T> interface; it would probably be good to use that as the declared return type.

    That does, however, leave open the question of what type of instance to return. One approach would be to clone all the items in the original collection. Another would be to always return a read-only wrapper. A third would be to return the original collection if it implements IReadOnlyCollection<T>. Each approach will be the best one in certain contexts, but will be less than ideal (or perhaps downright dreadful) in others. Unfortunately, Microsoft provides no standard means by which a question can be asked two very important questions:

    1. Do you promise to always and forevermore contain the same items as you do right now?

    2. Can you safely be exposed directly to code which is not supposed to modify your contents.

    Which style of wrapping is appropriate would depend upon what the client code is expecting to do with the thing it receives. Some scenarios to be avoided:

    1. An object was supplied of a type that the client would recognize as immutable, but rather than being returned directly it is duplicated, using a type that the client doesn't recognize as immutable. Consequently, the client is compelled to duplicate the collection again.

    2. An object was supplied of a type that the client would recognize as immutable, but before being returned it is wrapped in such a fashion that the client can't tell whether the collection is immutable or not, and thus is compelled to duplicate.

    3. An object of mutable type which is not supposed to be mutated is supplied by a client (cast to a read-only interface). It is then exposed directly to another client which determines that it is a mutable type and proceeds to modify it.

    4. A reference to a mutable collection is received and is encapsulated in a read-only wrapper before being returned to a client that needs an immutable object. The method that returned the collection promised that it is immutable, and thus the client declined to make its own defensive copy. The client is then ill-prepared for the possibility that the collection might change.

    There isn't really any particularly "safe" course of action an object can take with collections that it receives from some clients and needs to expose to others. Always duplicating everything is in many circumstances the safest course of action, but it can easily result in situations where a collection which shouldn't need to be duplicated at all ends up getting duplicated hundreds or thousands of times. Returning references as received can often be the most efficient approach, but it can also be semantically dangerous.

    I wish Microsoft would add a standard means by which collections could be asked the above questions or, better yet, be asked to produce an immutable snapshot of their current state. An immutable collection could return an immutable snapshot of its current state very cheaply (just return itself) and even some mutable collection types could return an immutable snapshot at a cost far below the cost of a full enumeration (e.g. a List<T> might be backed by two T[][] arrays, one of which holds references to sharable immutable arrays of 256 items, and the other of which holds references to unsharable mutable arrays of 256 items. Making a snapshot of a list would require cloning only the inner arrays containing items that have been modified since the last snapshot--potentially much cheaper than cloning the whole list. Unfortunately, since there's no standard "make an immutable snapshot" interface [note that ICloneable doesn't count, since a clone of a mutable list would be mutable; while one could make an immutable snapshot by encapsulating a mutable clone in a read-only wrapper, that would only work for things which are cloneable, and even types which aren't cloneable should still support a "mutable snapshot" function.]

    0 讨论(0)
  • 2020-12-16 02:56

    My recommendation is to return use a ReadOnlyCollection<T> for the scenario directly. This makes the usage explicit to the calling user.

    Normally I would suggest using the appropriate interface. But given that the .NET Framework does not currently have a suitable IReadOnlyCollection, you must go with the ReadOnlyCollection type.

    Also you must be aware when using ReadOnlyCollection, because it is not actually read-only: Immutability and ReadOnlyCollection

    0 讨论(0)
  • 2020-12-16 03:00

    Sometimes you may want to use an interface, perhaps because you want to mock the collection during unit testing. Please see my blog entry for adding your own interface to ReadonlyCollection by using an adapter.

    0 讨论(0)
  • 2020-12-16 03:05

    You can make "foos" a ReadOnlyCollection like this:

    ReadOnlyCollection<T> readOnlyCollection = foos.ToList<T>().AsReadOnly();
    

    Then you can expose it as a property of your class.

    EDIT:

        class FooContainer
        {
            private ICollection<Foo> foos;
            public ReadOnlyCollection<Foo> ReadOnlyFoos { get { return foos.ToList<Foo>().AsReadOnly();} }
    
        }
    

    Note: You should remember that once you get the ReadOnlyFoos collection is no longer "synchronized" with your foos ICollection. See the thread you referenced.

    0 讨论(0)
  • 2020-12-16 03:05

    Return a T[]:

    private ICollection<T> items;
    
    public T[] Items
    {
        get { return new List<T>(items).ToArray(); }
    }
    
    0 讨论(0)
  • 2020-12-16 03:07

    I seem to have settled on returning IEnumerable with the objects cloned.

    public IEnumerable<Foose> GetFooseList() {
       foreach(var foos in Collection) {
         yield return foos.Clone();
       }
    }
    
    • requires a Clone method on Foos.

    This allows no changes in the collection. Remember that ReadonlyCollection is "leaky" since the objects inside it can be changed as mentioned in a link in another post.

    0 讨论(0)
提交回复
热议问题