Ответы:
IEnumerable
не поддерживает это Это по замыслу. IEnumerable
использует ленивую оценку, чтобы получить элементы, которые вы запрашиваете, прежде чем они вам понадобятся.
Если вы хотите узнать количество элементов без перебора, которое вы можете использовать ICollection<T>
, у него есть Count
свойство.
Метод System.Linq.Enumerable.Count
расширения IEnumerable<T>
имеет следующую реализацию:
ICollection<T> c = source as ICollection<TSource>;
if (c != null)
return c.Count;
int result = 0;
using (IEnumerator<T> enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
result++;
}
return result;
Таким образом, он пытается привести к объекту, у ICollection<T>
которого есть Count
свойство, и использует его, если это возможно. В противном случае это повторяется.
Поэтому лучше всего использовать Count()
метод расширения для вашего IEnumerable<T>
объекта, так как вы получите максимально возможную производительность.
ICollection<T>
первую очередь.
T
на IEnumerator<T> enumerator = source.GetEnumerator()
, просто вы можете сделать это: IEnumerator enumerator = source.GetEnumerator()
и должны работать!
IEnumerable<T>
наследует, IDisposable
что позволяет using
оператору автоматически утилизировать его. IEnumerable
не. Так что, если вы позвоните в GetEnumerator
любом случае, вы должны закончить сvar d = e as IDisposable; if (d != null) d.Dispose();
Просто добавив дополнительную информацию:
Count()
Расширение не всегда итерации. Рассмотрим Linq to Sql, где счетчик отправляется в базу данных, но вместо возврата всех строк он выдает команду Sql Count()
и возвращает этот результат.
Кроме того, компилятор (или среда выполнения) достаточно умен, чтобы вызывать Count()
метод objects, если он у него есть. Так что это не так, как говорят другие респонденты, быть полностью невежественным и всегда повторять, чтобы подсчитать элементы.
Во многих случаях, когда программист просто проверяет, if( enumerable.Count != 0 )
используя Any()
метод расширения, if( enumerable.Any() )
это гораздо более эффективно при ленивой оценке linq, поскольку он может закорачивать, как только он может определить, есть ли какие-либо элементы. Это также более читабельно
.Count
свойство, поскольку оно всегда знает, какой у него размер. При запросе collection.Count
нет дополнительных вычислений, он просто возвращает уже известный счетчик. То же самое, Array.length
насколько я знаю. Однако .Any()
получает перечислитель источника с использованием using (IEnumerator<TSource> enumerator = source.GetEnumerator())
и возвращает true, если это возможно enumerator.MoveNext()
. Для коллекций:, if(collection.Count > 0)
массивов: if(array.length > 0)
и на перечислимых if(collection.Any())
.
IQueryably<T>
к a, IEnumerable<T>
он не выдаст счетчик sql. Когда я написал это, Linq to Sql был новым; Я думаю, что я просто настаивал на том, чтобы использовать, Any()
потому что для перечислений, коллекций и sql это было намного более эффективно и читабельно (в общем). Спасибо за улучшение ответа.
У моего друга есть серия постов в блоге, которые иллюстрируют, почему вы не можете этого сделать. Он создает функцию, которая возвращает IEnumerable, где каждая итерация возвращает следующее простое число, вплоть до ulong.MaxValue
следующего элемента и не рассчитывается до тех пор, пока вы его не попросите. Быстрый, поп-вопрос: сколько предметов возвращено?
Вот посты, но они вроде длинные:
EnumerableFeatures
объект), не требовало бы, чтобы счетчики выполняли какие-либо сложные задачи, но было бы в состоянии задать такие вопросы (наряду с некоторыми другими, такими как «Можете ли вы пообещать всегда возвращайте одну и ту же последовательность элементов »,« Можете ли вы безопасно подвергнуться воздействию кода, который не должен изменять вашу базовую коллекцию »и т. д.), было бы очень полезно
set yield options
заявление или нечто подобное для его поддержки. Если правильно спроектировать, он IEnhancedEnumerator
может сделать такие вещи, как LINQ, намного более удобными, исключая множество «защитных» ToArray
или ToList
вызовов, особенно ...
Enumerable.Concat
используются для объединения большой коллекции, которая много знает о себе, с маленькой, которая этого не делает.
IEnumerable не может рассчитывать без итерации.
При «нормальных» обстоятельствах классы, реализующие IEnumerable или IEnumerable <T>, такие как List <T>, могут реализовать метод Count, возвращая свойство List <T> .Count. Однако метод Count на самом деле не является методом, определенным в интерфейсе IEnumerable <T> или IEnumerable. (Фактически единственным является GetEnumerator.) И это означает, что для него не может быть обеспечена реализация для конкретного класса.
Скорее, Count это метод расширения, определенный для статического класса Enumerable. Это означает, что он может быть вызван для любого экземпляра производного класса IEnumerable <T>, независимо от реализации этого класса. Но это также означает, что он реализован в одном месте, вне любого из этих классов. Что, конечно, означает, что он должен быть реализован таким образом, который полностью независим от внутренних функций этих классов. Единственный способ подсчета - итерация.
В качестве альтернативы вы можете сделать следующее:
Tables.ToList<string>().Count;
Нет не вообще. Одним из аспектов использования перечислимых элементов является то, что фактический набор объектов в перечислении неизвестен (заранее или даже вообще).
Вы можете использовать System.Linq.
using System;
using System.Collections.Generic;
using System.Linq;
public class Test
{
private IEnumerable<string> Tables
{
get {
yield return "Foo";
yield return "Bar";
}
}
static void Main()
{
var x = new Test();
Console.WriteLine(x.Tables.Count());
}
}
Вы получите результат «2».
Выходя за рамки вашего непосредственного вопроса (на который был полностью дан отрицательный ответ), если вы хотите сообщить о прогрессе при обработке перечислимого, вы можете обратиться к моему сообщению в блоге « Отчет о ходе выполнения во время запросов Linq» .
Это позволяет вам сделать это:
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += (sender, e) =>
{
// pretend we have a collection of
// items to process
var items = 1.To(1000);
items
.WithProgressReporting(progress => worker.ReportProgress(progress))
.ForEach(item => Thread.Sleep(10)); // simulate some real work
};
Я использовал такой способ внутри метода, чтобы проверить переданный IEnumberable
контент
if( iEnum.Cast<Object>().Count() > 0)
{
}
Внутри такой метод:
GetDataTable(IEnumberable iEnum)
{
if (iEnum != null && iEnum.Cast<Object>().Count() > 0) //--- proceed further
}
bool hasContents = false; if (iEnum != null) foreach (object ob in iEnum) { hasContents = true; ... your code per ob ... }
.
Это зависит от того, какая версия .Net и реализация вашего объекта IEnumerable. Microsoft исправила метод IEnumerable.Count для проверки реализации и использует ICollection.Count или ICollection <TSource> .Count, подробности см. Здесь https://connect.microsoft.com/VisualStudio/feedback/details/454130.
И ниже MSIL от Ildasm для System.Core, в котором находится System.Linq.
.method public hidebysig static int32 Count<TSource>(class
[mscorlib]System.Collections.Generic.IEnumerable`1<!!TSource> source) cil managed
{
.custom instance void System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 )
// Code size 85 (0x55)
.maxstack 2
.locals init (class [mscorlib]System.Collections.Generic.ICollection`1<!!TSource> V_0,
class [mscorlib]System.Collections.ICollection V_1,
int32 V_2,
class [mscorlib]System.Collections.Generic.IEnumerator`1<!!TSource> V_3)
IL_0000: ldarg.0
IL_0001: brtrue.s IL_000e
IL_0003: ldstr "source"
IL_0008: call class [mscorlib]System.Exception System.Linq.Error::ArgumentNull(string)
IL_000d: throw
IL_000e: ldarg.0
IL_000f: isinst class [mscorlib]System.Collections.Generic.ICollection`1<!!TSource>
IL_0014: stloc.0
IL_0015: ldloc.0
IL_0016: brfalse.s IL_001f
IL_0018: ldloc.0
IL_0019: callvirt instance int32 class [mscorlib]System.Collections.Generic.ICollection`1<!!TSource>::get_Count()
IL_001e: ret
IL_001f: ldarg.0
IL_0020: isinst [mscorlib]System.Collections.ICollection
IL_0025: stloc.1
IL_0026: ldloc.1
IL_0027: brfalse.s IL_0030
IL_0029: ldloc.1
IL_002a: callvirt instance int32 [mscorlib]System.Collections.ICollection::get_Count()
IL_002f: ret
IL_0030: ldc.i4.0
IL_0031: stloc.2
IL_0032: ldarg.0
IL_0033: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<!!TSource>::GetEnumerator()
IL_0038: stloc.3
.try
{
IL_0039: br.s IL_003f
IL_003b: ldloc.2
IL_003c: ldc.i4.1
IL_003d: add.ovf
IL_003e: stloc.2
IL_003f: ldloc.3
IL_0040: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_0045: brtrue.s IL_003b
IL_0047: leave.s IL_0053
} // end .try
finally
{
IL_0049: ldloc.3
IL_004a: brfalse.s IL_0052
IL_004c: ldloc.3
IL_004d: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0052: endfinally
} // end handler
IL_0053: ldloc.2
IL_0054: ret
} // end of method Enumerable::Count
Вот отличная дискуссия о отложенной оценке и отложенном выполнении . В основном вы должны материализовать список, чтобы получить это значение.
Результат функции IEnumerable.Count () может быть неправильным. Это очень простой пример для тестирования:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections;
namespace Test
{
class Program
{
static void Main(string[] args)
{
var test = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 };
var result = test.Split(7);
int cnt = 0;
foreach (IEnumerable<int> chunk in result)
{
cnt = chunk.Count();
Console.WriteLine(cnt);
}
cnt = result.Count();
Console.WriteLine(cnt);
Console.ReadLine();
}
}
static class LinqExt
{
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, int chunkLength)
{
if (chunkLength <= 0)
throw new ArgumentOutOfRangeException("chunkLength", "chunkLength must be greater than 0");
IEnumerable<T> result = null;
using (IEnumerator<T> enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
result = GetChunk(enumerator, chunkLength);
yield return result;
}
}
}
static IEnumerable<T> GetChunk<T>(IEnumerator<T> source, int chunkLength)
{
int x = chunkLength;
do
yield return source.Current;
while (--x > 0 && source.MoveNext());
}
}
}
Результат должен быть (7,7,3,3), но фактический результат (7,7,3,17)
Лучший способ, который я нашел, - считать, преобразовав его в список.
IEnumerable<T> enumList = ReturnFromSomeFunction();
int count = new List<T>(enumList).Count;
Я бы предложил вызвать ToList. Да, вы делаете перечисление рано, но у вас все еще есть доступ к вашему списку предметов.
Это может не дать наилучшей производительности, но вы можете использовать LINQ для подсчета элементов в IEnumerable:
public int GetEnumerableCount(IEnumerable Enumerable)
{
return (from object Item in Enumerable
select Item).Count();
}
Я думаю, что самый простой способ сделать это
Enumerable.Count<TSource>(IEnumerable<TSource> source)
Ссылка: system.linq.enumerable
Я пользуюсь IEnum<string>.ToArray<string>().Length
и все отлично работает.
IEnum<string>.Count();
"?
Я использую такой код, если у меня есть список строк:
((IList<string>)Table).Count