private IEnumerable<string> Tables
{
get
{
yield return "Foo";
yield return "Bar";
}
}
比方說,我想對這些進行迭代,然後編寫#m處理#n。從IEnumerable <T>中統計項目而不進行迭代?
有沒有一種方法,我可以找出m的價值,而無需在我的主迭代之前進行迭代?
我希望我明確自己。
private IEnumerable<string> Tables
{
get
{
yield return "Foo";
yield return "Bar";
}
}
比方說,我想對這些進行迭代,然後編寫#m處理#n。從IEnumerable <T>中統計項目而不進行迭代?
有沒有一種方法,我可以找出m的價值,而無需在我的主迭代之前進行迭代?
我希望我明確自己。
IEnumerable
不支持這一點。這是設計。 IEnumerable
使用懶惰評估來獲取您在需要之前請求的元素。
如果您想知道沒有迭代它們的物品數量,您可以使用ICollection<T>
,它具有Count
屬性。
不,一般不會。使用枚舉數的一個要點是枚舉中的實際對象集並不知道(事先或甚至完全不知道)。
您帶來了最重要的一點是,即使你得到的IEnumerable的對象,你必須看到,如果你能投它弄清楚它是什麼類型。對於那些試圖在我的代碼中使用更多IEnumerable的人來說,這是非常重要的一點。 – PositiveGuy 2010-08-18 15:35:34
編號
您是否看到在您編寫的代碼中的任何位置可用的信息?
您可能會爭辯說,編譯器可以「看到」只有兩個,但這意味着它需要分析每個迭代器方法,只是針對該特定的病例。即使它確實如此,考慮到IEnumerable的限制,你會如何閱讀它?
我的一位朋友有一系列的博客文章,提供了爲什麼你不能做到這一點的插圖。他創建的函數返回一個IEnumerable,其中每次迭代都會返回下一個素數,一直到ulong.MaxValue
,並且下一個項目不會計算,直到您請求爲止。快速,流行的問題:有多少物品被退回?
這裏有崗位,但他們那種長:
IEnumerable無法迭代計數。
在「正常」的情況下,將對於如List <Ť>實現了IEnumerable或IEnumerable的<Ť>類,是可能的,通過返回列表<Ť> .Count之間的屬性來實現的計數方法。但是,Count方法實際上並不是在IEnumerable <T>或IEnumerable接口上定義的方法。 (實際上,唯一一個是GetEnumerator。)這意味着無法爲它提供特定於類的實現。
相反,它是一個擴展方法,定義在靜態類Enumerable上。這意味着可以在任何IEnumerable派生類的實例上調用它,而不管該類的實現如何。但這也意味着它在任何一個類別的外部實施。這當然意味着它必須以完全獨立於這些類的內部的方式來實現。唯一這種做計數的方法是通過迭代。
只是增加了額外的一些信息:
的Count()
擴展並不總是迭代。考慮Linq to Sql,其中計數到達數據庫,但不是將所有行都帶回來,而是發出Sql Count()
命令並返回該結果。
此外,編譯器(或運行時)足夠聰明,如果它有一個,它將調用對象Count()
方法。所以它的而不是正如其他響應者所說,完全無知並且總是迭代以計算元素。
在許多情況下程序員使用Any()
擴展方法,因爲它可以短路,一旦它能夠確定存在任何元件如if(enumerable.Any())
更爲高效與LINQ的懶惰評價只是檢查if(enumerable.Count != 0)
。它也更具可讀性
我建議調用ToList。是的,您正在儘早進行枚舉,但您仍然可以訪問您的項目列表。
或者,你可以做到以下幾點:
Tables.ToList<string>().Count;
這裏是一個大討論關於lazy evaluation和deferred execution。基本上你必須實現清單才能獲得這個價值。
的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
財產,並使用該如果可能的話。否則它迭代。
所以你最好的選擇是在你的IEnumerable<T>
對象上使用Count()
擴展方法,因爲你將以這種方式獲得最佳性能。
超出您的直接問題(已經徹底回答否定),如果您希望在處理枚舉數時報告進度,您可能需要查看我的博客文章Reporting Progress During Linq Queries。
它可以讓你這樣做:在IEnumerable.Count()函數的
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
};
結果可能是錯誤的。這是一個非常簡單的樣品進行測試:
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)
這取決於哪個版本.Net和您的IEnumerable對象的實現。 微軟已經修復了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的,但如果你需要的數量也就那麼我的首選方法是使用這個接口:如果您原來的集合不具有任何索引
public interface IEnumAndCount<out T> : IEnumerable<T>
{
int Count { get; }
}
,你的計數實現可以遍歷集合,並具有已知的性能O(n)。
如果你不想使用類似於IEnumAndCount的東西,你最好的選擇是和Linq.Count一起去看Daniel Earwicker給出的問題。
祝你好運!
我使用IEnum<string>.ToArray<string>().Length
它工作正常。
您可以使用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'。
我用這樣的方式方法裏面來檢查IEnumberable
內容
if(iEnum.Cast<Object>().Count() > 0)
{
}
傳遞內部的方法是這樣的:
GetDataTable(IEnumberable iEnum)
{
if (iEnum != null && iEnum.Cast<Object>().Count() > 0) //--- proceed further
}
它可能不會產生最佳性能,但可以使用LINQ來計算IEnumerable中的元素:
public int GetEnumerableCount(IEnumerable Enumerable)
{
return (from object Item in Enumerable
select Item).Count();
}
你不能沒有inte至少是至少一次迭代。這將工作。
using System.Linq;
...
Tables.Count();
如果您不需要通過索引器訪問列表,我希望ICollection優先於IList。 – 2008-10-03 21:17:20
我通常只是抓住List和IList的習慣。但是特別是如果你想自己實現它們,ICollection更容易,同時也具有Count屬性。謝謝! – Mendelt 2008-10-03 22:05:20
那麼當我有給定的IEnumerable時,如何檢查計數? – Shimmy 2009-11-30 23:40:31