Відповіді:
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 до Sql, де підрахунок переходить до бази даних, але замість того, щоб повернути всі рядки, він видає SqlCount()
і повертає цей результат замість цього.
Крім того, компілятор (або час виконання) досить розумний, що він буде викликати Count()
метод об'єктів, якщо він має. Тож це не так, як кажуть інші респонденти, будучи абсолютно неосвіченими та завжди повторюючими, щоб рахувати елементи.
У багатьох випадках, коли програміст просто перевіряє 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>
команду, IEnumerable<T>
вона не видасть sql. Коли я писав це, Linq до Sql був новим; Я думаю, що я просто намагався використовувати, Any()
тому що для перелічених номерів, колекцій та sql це було набагато ефективніше і читабельніше (загалом). Дякуємо за вдосконалення відповіді.
У мого друга є низка публікацій в блогах, які пояснюють, чому ви не можете цього зробити. Він створює функцію, яка повертає IEnumerable, коли кожна ітерація повертає наступне просте число, аж до ulong.MaxValue
наступного, і наступний елемент не обчислюється, поки ви не попросите цього. Швидке запитання: скільки предметів повертається?
Ось повідомлення, але вони такі довгі:
EnumerableFeatures
об’єкта), не вимагатиме від чисельників нічого складного, але спроможного задавати такі питання (поряд із деякими іншими на кшталт "Чи можете ви пообіцяти завжди повертайте ту саму послідовність елементів "," Чи можете ви безпечно потрапляти до коду, який не повинен змінювати вашу базову колекцію "тощо), було б дуже корисно.
set yield options
заяву або щось подібне, щоб підтримати її. Якщо правильно розроблено, IEnhancedEnumerator
може зробити такі речі, як LINQ, набагато кориснішими, усунувши багато "оборонних" ToArray
або ToList
викликів, особливо ...
Enumerable.Concat
використовуються для комбінування великої колекції, яка багато чого знає про себе, і маленької, яка ні.
Численні не можуть рахувати без повторення.
За "звичайних" обставин, для класів, що реалізують 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.численні
Я використовую, IEnum<string>.ToArray<string>().Length
і це чудово працює.
IEnum<string>.Count();
"?
Я використовую такий код, якщо у мене є список рядків:
((IList<string>)Table).Count