Имеет ли dot-сеть интерфейс, такой как IEnumerable с свойством Count?

Имеет ли точка net интерфейс, такой как IEnumerable с свойством count? Я знаю об интерфейсах, таких как IList и ICollection, которые предлагают свойство Count, но похоже, что эти интерфейсы были сначала разработаны для изменяемых структур данных, а использование в качестве интерфейса только для чтения кажется запоздалым - наличие поля IsReadOnly и мутаторов, бросающих исключения когда это свойство верно, IMO является достаточным доказательством этого.

В настоящее время я использую настраиваемый интерфейс IReadOnlyCollection (см. мой собственный ответ на этот пост), но я был бы рад узнать о других альтернативных подходах.

Ответ 1

Ключевое различие между семейством ICollection и семейством IEnumerable заключается в отсутствии уверенности относительно количества присутствующих предметов (нередко элементы будут сгенерированы/загружены/гидратированы по мере необходимости) - в некоторых случаях Enumerable не может когда-либо заканчивая получение результатов, поэтому граф отсутствует.

Получение и добавление графа возможно в зависимости от ваших требований, но оно противоречит этому духу, который является целью ICollection - совокупности вещей, которые там есть.

Другим способом может быть использование метода System.Linq.Enumerable.Count, т.е.

using System.Linq;

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

или используйте (System.Linq.Enumerable).ToList(), чтобы вытащить все элементы из перечислителя в коллекцию и работать оттуда.

(Также, чтобы ответить на ваш комментарий перед тем, как иметь 50 rep: бит бит .Count() - это вызов метода расширения в классе расширения System.Linq.Enumerable - метод расширения доступен для всех вещей, которые выводятся из IEnumerable, потому что у кода есть "использование System.Linq", который привносит методы расширения во все классы в этом пространстве имен в область видимости - в этом случае это в классе Enumerable. Если вы находитесь в VS, нажатие F12 приведет вас к определение SLEnumerable. BTW С# In Depth - это фантастическая книга для обучения LINQ правильно - ее токарь страницы действительно помогает вам получить всю картину по сравнению с изучением бит LINQ по частям)

Ответ 2

Как и для .Net 4.5, для этого есть два новых интерфейса: IReadOnlyCollection<T> и IReadOnlyList<T>.

IReadOnlyCollection<T> есть IEnumerable<T> с добавленным свойством Count, IReadOnlyList<T> также добавляет индексирование.

Ответ 3

Принимая во внимание некоторые комментарии, я решил пойти с классом-оболочкой, реализующим пользовательский интерфейс...

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

Спасибо всем за ваши предложения.

Изменить: Теперь нельзя использовать недопустимое преобразование в список (или что-то еще).

Ответ 4

Похоже, вы действительно хотите ReadOnlyCollection<T> - выставить его как IList<T>, но, обернув исходный список, как это вы просто получите оболочку только для чтения с соответствующим счетчиком.

Ответ 5

IList может возвращать IsReadOnly как true, что отмечает коллекцию как readonly. Кроме этого, я боюсь, я не знаю ничего подходящего.

Ответ 6

Поскольку это интерфейс, вам нужно было бы реализовать свойство Count самостоятельно, почему бы вам не создать новый интерфейс, который наследует IEnumerator и добавить свойство Count?

Ответ 7

IList или ICollection - это путь, если вы хотите использовать стандартные интерфейсы.

Обратите внимание, что вы можете "спрятать" методы, требуемые интерфейсом, если вы не хотите их в открытом интерфейсе вашего класса - например, поскольку бессмысленно добавлять вещи в коллекцию readonly, вы можете это сделать:

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

и др.

Ответ 8

Вы можете получить. Включите IEnumerable с помощью метода расширения, если вы добавите ссылку на System.Linq(в любом случае 3.5).

Ответ 9

Массив может быть передан в IList, что делает IList ReadOnly == true:)

Ответ 10

Как упоминает Джон Скит, вам гораздо лучше использовать System.Collections.ObjectModel.ReadOnlyCollection вместо создания собственного класса-оболочки.

Затем вы можете реализовать свой образец следующим образом:

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