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

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.


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.


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();
}