LWChris LWChris - 2 months ago 10
C# Question

How to turn ICollection<T> into IReadOnlyCollection<T>?

When I have a variable of

ICollection<T>
in C#, I cannot pass it to a function that expects an
IReadOnlyCollection<T>
:

public void Foo()
{
ICollection<int> data = new List<int>();
// Bar(data); // Not allowed: Cannot implicitly cast ICollection<int> to IReadOnlyCollection<int>
Bar(data.ToList()); // Works, since List<T> implements IReadOnlyCollection<T>
}

public void Bar(IReadOnlyCollection<int> data)
{
if (data.Count == 1) { /* ... */ }
// ...
}


Apparently the problem is that
ICollection<T>
does not inherit from
IReadOnlyCollection<T>
- but why?
ICollection<T>
should be the full functional set of
IReadOnlyCollection<T>
plus the functions that modify the collection.

And what is the best solution to pass the arguments?

On the one hand, since I don't want to alter the collection in
Bar
and just need the count and iterate over the collection, I'd like to require an
IReadOnlyCollection
.

On the other hand, I don't want to create a new list object every time I call that function.

Answer

There is no standard solution AFAIK, but it's not hard to make your own like this

public static class MyExtensions
{
    public static IReadOnlyCollection<T> AsReadOnly<T>(this ICollection<T> source)
    {
        if (source == null) throw new ArgumentNullException("source");
        return source as IReadOnlyCollection<T> ?? new ReadOnlyCollectionAdapter<T>(source);
    }
    sealed class ReadOnlyCollectionAdapter<T> : IReadOnlyCollection<T>
    {
        ICollection<T> source;
        public ReadOnlyCollectionAdapter(ICollection<T> source) { this.source = source; }
        public int Count { get { return source.Count; } }
        public IEnumerator<T> GetEnumerator() { return source.GetEnumerator(); }
        IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
    }
}

And then use it as follows

Bar(data.AsReadOnly());
Comments