回答:
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を考えますが、すべての行Count()
を戻すのではなく、Sql コマンドを発行してその結果を返します。
さらに、コンパイラー(またはランタイム)は、objects Count()
メソッドがある場合はそれを呼び出すように十分にスマートです。だから、だではない、完全に無知であることと、常に要素をカウントするために反復し、他のレスポンダが言うように。
linqの遅延評価では、要素があると判断できると短絡する可能性があるため、プログラマーが拡張メソッドif( enumerable.Count != 0 )
を使用してチェックしている多くの場合ははるかに効率的です。また、読みやすくなりますAny()
if( enumerable.Any() )
.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 to Sqlは新しいものでした。Any()
列挙型、コレクション、SQLの方が(一般的に)はるかに効率的で読みやすいので、私は使用することを強く求めていたと思います。答えを改善してくれてありがとう。
私の友人は、あなたがこれを行うことができない理由を説明する一連のブログ投稿を持っています。彼は、IEnumerableを返す関数を作成します。ここで、各反復は次の素数を返し、にulong.MaxValue
至るまで、次の項目は要求されるまで計算されません。簡単でポップな質問:返されるアイテムの数は?
ここに投稿がありますが、長いです:
EnumerableFeatures
オブジェクトを返すようにする)、列挙子は難しいことをする必要はありませんが、そのような質問をすることができます(「あなたは約束できますか?常に同じアイテムのシーケンスを返す」、「基になるコレクションを変更してはならないコードに安全にさらされることはできます」など)は非常に便利です。
set yield options
それをサポートするステートメントまたは類似のものを取得します。適切に設計されていればIEnhancedEnumerator
、多くの「防御的な」呼び出しToArray
やToList
呼び出しを排除することで、LINQなどをより使いやすくすることができます。特に...
Enumerable.Concat
が、それ自体についてよく知っている大きなコレクションと、知らない小さなコレクションを組み合わせるために使用される場合。
IEnumerableは、反復なしではカウントできません。
「通常の」状況では、List <T>などのIEnumerableまたはIEnumerable <T>を実装するクラスが、List <T> .Countプロパティを返すことによってCountメソッドを実装することが可能です。ただし、Countメソッドは、実際にはIEnumerable <T>またはIEnumerableインターフェイスで定義されたメソッドではありません。(実際には、唯一のGetEnumeratorです。)これは、クラス固有の実装を提供できないことを意味します。
Countは、静的メソッドEnumerableで定義された拡張メソッドです。つまり、そのクラスの実装に関係なく、IEnumerable <T>派生クラスの任意のインスタンスで呼び出すことができます。ただし、これらのクラスの外部の単一の場所に実装されることも意味します。もちろんこれは、これらのクラスの内部から完全に独立した方法で実装する必要があることを意味します。カウントを行う唯一のそのような方法は、反復によるものです。
または、次の操作を実行できます。
Tables.ToList<string>().Count;
いいえ、一般的ではありません。enumerableを使用する際の1つのポイントは、列挙内の実際のオブジェクトのセットが(事前に、またはまったく)不明であることです。
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を参照してください
そして以下は、System.Linqが存在するSystem.Core用のIldasmからのMSILです。
.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)です
最高のパフォーマンスは得られない可能性がありますが、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();
「?