Sandeep Datta
Sandeep Datta

Reputation: 29355

Does dot net have an interface like IEnumerable with a Count property?

Does dot net have an interface like IEnumerable with a count property? I know about interfaces such as IList and ICollection which do offer a Count property but it seems like these interfaces were designed for mutable data structures first and use as a read only interface seems like an afterthought - the presence of an IsReadOnly field and mutators throwing exceptions when this property is true is IMO ample evidence for this.

For the time being I am using a custom interface called IReadOnlyCollection (see my own answer to this post) but I would be glad to know of other alternative approaches.

Upvotes: 14

Views: 5285

Answers (10)

Sandeep Datta
Sandeep Datta

Reputation: 29355

Taking into consideration some of the comments I have decided to go with a wrapper class implementing a custom interface...

interface IReadOnlyCollection<T> : IEnumerable<T>
{
    int Count { get; }
}

//This can now be not misused by downcasting to List
//The wrapper can also be used with lists since IList inherits from ICollection
public class CollectionWrapper<T> : IReadOnlyCollection<T>
{

    public CollectionWrapper(ICollection<T> collection)
    {
        _collection = collection;
    }

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

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

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

    ////////Private data///////
    ICollection<T> _collection;
}


class Program
{
    static void Main(string[] args)
    {
        List<int> list = new List<int>();

        list.Add(1);
        list.Add(2);
        list.Add(3);
        list.Add(4);

        CollectionWrapper<int> collection = new CollectionWrapper<int>(list);

        Console.WriteLine("Count:{0}", collection.Count);
        foreach (var x in collection)
        {
            Console.WriteLine(x);
        }

        foreach (var x in (IEnumerable)collection)
        {
            Console.WriteLine(x);
        }
    }
}

Thanks all for your suggestions.

Edit: Now cannot be misused by downcasting to List (or whatever).

Upvotes: 4

svick
svick

Reputation: 245076

As of .Net 4.5, there are two new interfaces for this: IReadOnlyCollection<T> and IReadOnlyList<T>.

IReadOnlyCollection<T> is IEnumerable<T> with a Count property added, IReadOnlyList<T> also adds indexing.

Upvotes: 11

Ruben Bartelink
Ruben Bartelink

Reputation: 61903

The key difference between the ICollection family and the IEnumerable family is the absence of certainty as to the count of items present (quite often the items will be generated/loaded/hydrated as needed) - in some cases, an Enumerable may not ever finish generating results, which is why the Count is missing.

Deriving and adding a Count is possible depending on your requirements, but it goes against this spirit, which is the purpose of ICollection - a collection of stuff that's all there.

Another way might be to use the System.Linq.Enumerable.Count method, i.e.

using System.Linq;

class X
{
   void Y(IEnumerable<int> collection)
   {
      int itemCount = collection.Count();
   }
}

or use the (System.Linq.Enumerable) .ToList() to pull all the items from the enumerator into a Collection and work from there.

(Also to answer your comment before having 50 rep:- the ".Count()" bit is a call to an extension method on the extension class System.Linq.Enumerable - the extension method is available on all things that derive from IEnumerable because the code has a "using System.Linq" which brings the extension methods in all classes in that namespace into scope - in this case its in the class Enumerable. If you're in VS, pressing F12 will bring you to the definition of S.L.Enumerable. BTW C# In Depth is a fantastic book for learning LINQ properly - its a page turner thats really helps you get the whole picture compared to learning the bits of LINQ piece by piece)

Upvotes: 14

Justin Van Patten
Justin Van Patten

Reputation: 801

As Jon Skeet mentions, you're much better off using System.Collections.ObjectModel.ReadOnlyCollection instead of creating your own wrapper class.

Then you can implement your sample as follows:

class Program {
    static void Main(string[] args) {
        List<int> list = new List<int>();
        list.Add(1);
        list.Add(2);
        list.Add(3);
        list.Add(4);

        ReadOnlyCollection<int> collection = new ReadOnlyCollection<int>(list);
        Console.WriteLine("Count:{0}", collection.Count);
        foreach (var x in collection) {
            Console.WriteLine(x);
        }
        foreach (var x in (IEnumerable)collection) {
            Console.WriteLine(x);
        }
    }
}

Upvotes: 0

NikolaiDante
NikolaiDante

Reputation: 18649

You can get .Count on IEnumerable with an extension method if you add a reference to System.Linq (in 3.5 anyway).

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1504182

It sounds like you really just want ReadOnlyCollection<T> - expose it as IList<T>, but by wrapping the original list like this you just get a read-only wrapper with an appropriate count.

Upvotes: 4

Sekhat
Sekhat

Reputation: 4489

An array can be cast to an IList, which makes the IList ReadOnly == true :)

Upvotes: 0

Miral
Miral

Reputation: 13095

IList or ICollection would be the way to go, if you want to use the standard interfaces.

Note that you can "hide" methods required by the interface if you don't want them in your class's public interface -- for example, since it's meaningless to add things to a readonly collection you can do this:

void ICollection<DataType>.Add(DataType item)
{
    throw new NotSupportedException();
}

public DataType this[int index]
{
    get { return InnerList[index]; }
}

DataType IList<DataType>.this[int index]
{
    get { return this[index]; }
    set { throw new NotSupportedException(); }
}

etc.

Upvotes: 1

Bruno
Bruno

Reputation: 4527

Since it's an interface, you would have to implement the Count property yourself, why don't you create a new interface that inherits IEnumerator and add a Count property?

Upvotes: 2

Lasse V. Karlsen
Lasse V. Karlsen

Reputation: 391734

IList can return IsReadOnly as true, which marks the collection as readonly. Other than that I'm afraid I don't know of anything fitting.

Upvotes: 2

Related Questions