Как получить согласованное .Свойство Count / .Length для универсальных коллекций C #?

#c# #generics #collections

#c# #общие #Коллекции

Вопрос:

List<T> вместо этого имеет .Count свойство, где в виде T<> массивов .Length . Я предполагаю, что это связано с тем, что массивы имеют фиксированную длину, тогда как другие — нет, но разница в синтаксисе все еще может расстраивать.

Поэтому, если вы выполняете рефакторинг из массива в список, он выдает «не содержит определения для .Ошибки «Length», и кажется пустой тратой времени на его изменение, когда .Count и .Length по сути одинаковы.

Есть ли хороший способ справиться с этим? Можно ли расширить, List<T> чтобы добавить .Length свойство, которое является псевдонимом для .Count , например, и наоборот для универсального массива? И будет ли это плохой идеей по какой-либо причине?

Ответ №1:

Вы можете использовать Count метод, предоставляемый LINQ.

Это оптимизировано для использования Count свойства, предоставляемого ICollection<T> интерфейсом, где это возможно (или не универсального ICollection интерфейса тоже в .NET 4). Таким образом, массивы List<T> и т.д. Будут оптимизированы.

 var yourList = new List<string> { "the", "quick", "brown", "fox" };
int count1 = yourList.Count();  // uses the ICollection<T>.Count property

var yourArray = new[] { 1, 2, 4, 8, 16, 32, 64, 128 };
int count2 = yourArray.Count();  // uses the ICollection<T>.Count property

var yourEnumerable = yourArray.Where(x => x > 42);
int count3 = yourEnumerable.Count();  // no optimisation, iterates the sequence
  

В качестве альтернативы, если вам нужно какое-либо согласованное свойство count без риска повторения всей последовательности в неоптимизированном случае, вы могли бы создать свои собственные методы расширения. (Лично я бы не пошел по этому пути.)

 int count4 = yourList.GetCount();  // uses the ICollection<T>.Count property
int count5 = yourArray.GetCount();  // uses the ICollection<T>.Count property
int count6 = yourEnumerable.GetCount();  // compile-time error

// ...

public static class CollectionExtensions
{
    public static int GetCount<T>(this ICollection<T> source)
    {
        if (source == null) throw new ArgumentNullException("source");
        return source.Count;
    }

    public static int GetCount(this ICollection source)
    {
        if (source == null) throw new ArgumentNullException("source");
        return source.Count;
    }
}
  

Комментарии:

1. Кроме того, любая коллекция, которая реализует не универсальное ICollection . И следует отметить, что массивы реализуют оба интерфейса, поэтому они получают преимущество логики сокращения.

2. Не было ли у этого проблем с производительностью до .NET 4? Я думал, что это выполняется в цикле (даже без предиката), и поэтому потенциально было существенно медленнее. Простое выполнение Google «linq count slow» дает довольно много примеров. Похоже, это было бы решением для .Net 4, хотя и с оптимизациями.

3. @mikel: Насколько мне известно, Нет. Оно не оптимизировалось для не-универсальных коллекций ICollection до .NET 4, но я бы предположил, что для коллекции было бы относительно необычно реализовать generic IEnumerable<T> , не-generic ICollection , но не generic ICollection<T> , что и потребовалось бы для того, чтобы новая ICollection оптимизация вступила в силу там, где стандартная ICollection<T> оптимизация этого не сделала бы. (Конечно, Count метод всегда будет немного медленнее, чем прямое использование любого базового Count свойства, но ничего такого, что вы заметили бы при обычном повседневном использовании.)

4. Но OP спрашивает, почему a List не содержит Length свойства.. Count делает то же самое, но Length имеет O (1) временную завершенность

Ответ №2:

Вы могли бы поместить свой массив в переменную типа IList<T> . (массивы реализуют этот интерфейс)

Затем вы можете использовать массив точно так же, как вы используете любой другой IList<T> (хотя Add и Remove будут генерироваться исключения, поскольку массивы имеют фиксированную длину)

Ответ №3:

Вы можете использовать .Count() метод как для списков, так и для массивов.

.Count() Метод выполняется, если он доступен, затем он возвращается к традиционному .Length .

Ответ №4:

Вы могли бы просто расширить общий список и добавить метод count, однако, если причина этого просто в том, что вы повторно разлагаете и не хотите обновлять до Count, я не советую этого делать. Нет необходимости расширять класс, если вы на самом деле ничего к нему не добавляете. Почему бы просто не обновить свой код, чтобы использовать Count вместо Length?