Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a ReadOnlyCollection from a HashSet without copying the elements?

I have a private HashSet<string> which is the backing field of a read-only property which should return a read-only collection such that callers cannot modify the collection. So I tried to:

public class MyClass
{
    private readonly HashSet<string> _referencedColumns;

    public ICollection<string> ReferencedColumns { 
        get { return new ReadOnlyCollection<string>(_referencedColumns); }
    }

This does not compile as ReadOnlyCollection accepts a IList<T> which is not implememted by HashSet<T>. Is there another wrapper I can use to save me from copy the items? For my purpose it is enough to just return something implementing ICollection<T> (instead of IList<T>) which is implemented by the HashSet<T>.

like image 476
Dejan Avatar asked Apr 28 '16 18:04

Dejan


People also ask

How do I make a HashSet read only?

How To Make HashSet Read Only In Java? Collections. unmodifiableSet() method is used to create read only HashSet in java. Below program demonstrates that you will not be able to perform modification operations on read only set and modifications to original set are reflected in read only set also.

What is read only collection c#?

C# ReadOnlyCollectionUse the ReadOnlyCollection type from System. ReadOnlyCollection makes an array or List read-only. With this type from System. Collections. ObjectModel, we provide a collection of elements that cannot be changed.

Is a readonly collection mutable?

The fact that ReadOnlyCollection is immutable means that the collection cannot be modified, i.e. no objects can be added or removed from the collection.


3 Answers

Consider exposing the property as the type IReadOnlyCollection<> instead, which will provide a read-only view of the HashSet<>. This is an efficient way of implementing this, since the property getter will not require a copy of the underlying collection.

This will not prevent someone from casting the property to a HashSet<> and modifying it. If you are concerned with that, consider return _referencedColumns.ToList() in the property getter, which will create a copy of your underlying set.

like image 101
Bas Avatar answered Nov 03 '22 19:11

Bas


You can use the following decorator to wrap the hash set and return an ICollection<T> that is read-only (the IsReadOnly property returns true and modification methods throw a NotSupportedException as specified in the contract of ICollection<T>):

public class MyReadOnlyCollection<T> : ICollection<T>
{
    private readonly ICollection<T> decoratedCollection;

    public MyReadOnlyCollection(ICollection<T> decorated_collection)
    {
        decoratedCollection = decorated_collection;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return decoratedCollection.GetEnumerator();
    }

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

    public void Add(T item)
    {
        throw new NotSupportedException();
    }

    public void Clear()
    {
        throw new NotSupportedException();
    }

    public bool Contains(T item)
    {
        return decoratedCollection.Contains(item);
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        decoratedCollection.CopyTo(array, arrayIndex);
    }

    public bool Remove(T item)
    {
        throw new NotSupportedException();
    }

    public int Count
    {
        get { return decoratedCollection.Count; }
    }

    public bool IsReadOnly
    {
        get { return true; }
    }
}

And you can use it like this:

public class MyClass
{
    private readonly HashSet<string> _referencedColumns;

    public ICollection<string> ReferencedColumns { 
        get { return new MyReadOnlyCollection<string>(_referencedColumns); }
    }
    //...

Please note that this solution will not take a snapshot of the HashSet, instead it will hold a reference to the HashSet. This means that the returned collection will contain a live version of the HashSet, i.e., if the HashSet is changed, the consumer that obtained the read only collection before the change would be able to see the change.

like image 24
Yacoub Massad Avatar answered Nov 03 '22 21:11

Yacoub Massad


While it is not readonly, Microsoft released a nuget package called System.Collections.Immutable that contains an ImmutableHashSet<T> which implements IImmutableSet<T> which extendsIReadOnlyCollection<T>

Quick usage sample :

public class TrackedPropertiesBuilder : ITrackedPropertiesBuilder
{
    private ImmutableHashSet<string>.Builder trackedPropertiesBuilder;

    public TrackedPropertiesBuilder()
    {
        this.trackedPropertiesBuilder = ImmutableHashSet.CreateBuilder<string>();
    }

    public ITrackedPropertiesBuilder Add(string propertyName)
    {
        this.trackedPropertiesBuilder.Add(propertyName);
        return this;
    }

    public IImmutableSet<string> Build() 
        => this.trackedPropertiesBuilder.ToImmutable();
}
like image 20
Uwy Avatar answered Nov 03 '22 19:11

Uwy