简体   繁体   中英

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.

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, ie

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 SLEnumerable. 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)

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.

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).

听起来你真的只是想要ReadOnlyCollection<T> - 将它暴露为IList<T> ,但通过像这样包装原始列表,你只需得到一个具有适当计数的只读包装器。

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.

既然它是一个接口,你必须自己实现Count属性,为什么不创建一个继承IEnumerator并添加Count属性的新接口?

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.

如果添加对System.Linq的引用(无论如何都是3.5),你可以使用扩展方法在IEnumerable上获取.Count。

一个数组可以转换为IList,这使得IList ReadOnly == true :)

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM