.NET で IEnumerable が空かどうかを判断するための、堅牢でエレガントで再利用可能なコードは何ですか?
-
02-07-2019 - |
質問
IEnumerableかどうかを判断するために、最も再利用可能でありながらエレガントなコードを見つけようとしています。理想的には、これは、IEnumerable が空かどうかを確認する必要があるときにいつでも呼び出すことができる関数である必要があります。
これまでのところうまく機能している .NET 3.5 の答えを開発しましたが、IEnumerable は基礎となる結果を変更するコレクション (または反復子のキュー) を技術的にカプセル化できるため、完璧な答えは存在しないというのが私の現在の考えです。それが繰り返されるため、問題が発生します。ただし、これは IEnumerable.Count() の実装の障害にもなり、MS が IEnumerable.Count() を提供することを停止することはありませんでした。
そこで、誰かがより良いものを持っているかどうかを確認するために、そして他の誰かがそれを役立つと思うかもしれない場合に備えて、それをSOに投稿しようと思いました。
編集: わあ、IEnumerable.Any について知らなかったなんて信じられません。私はそれが存在することは知っていましたが、それが何をするのかをわざわざ確認することはありませんでした。これを教訓にしましょう。ドキュメントを読んでください。メソッド名が目的の動作を暗示していないからといって、それが希望の動作をしないというわけではありません。
解決
!enumerable.Any()
最初の要素のみを取得しようとします。
これがどのように/なぜ機能するのかを詳しく説明すると、any は、 IEnumerable
指定された関数に一致します。何も指定されない場合、コンポーネントは成功します。つまり、要素が列挙可能オブジェクトに存在する場合、関数は true を返します。
他のヒント
.net 1/2 の場合:
IEnumerator e;
try
{
e = enumerable.GetEnumerator();
return e.MoveNext();
}
finally
{
if (e is IDisposable)
e.Dispose();
}
または、ジェネリックを使用すると、次のようになります。
using (IEnumerator<T> e = enumerable.GetEnumerator())
{
return e.MoveNext();
}
完璧な答えはないというのは正しいです。IEnumerable は反復のみをサポートし、列挙が反復可能であることは保証しません。MoveNext を少なくとも 1 回呼び出さない限り、列挙型に要素が含まれているかどうかを確認することはできません。また、一度呼び出した後は、列挙型を再利用できるかどうかは保証できません。IEnumerable.Reset が NotSupportedException をスローすることは許容されます。から http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.reset.aspx:
「Reset メソッドは COM の相互運用性のために提供されています。必ずしも実装する必要はありません。代わりに、実装者は単純に NotSupportedException をスローできます。」
IEnumerable<T>.Count や IEnumerable<T>.Any などの拡張メソッドは、内部で MoveNext を呼び出す必要があります。便利なラッパーですが、列挙型がリセットをサポートしていない (まれな) ケースでは、問題が発生する可能性があるという事実を避けてはいけません。
これらのメソッドのいずれでも注意すべき点は、すべての列挙をロールバックできるわけではないことです。たとえば、System.Data.IDataReader のすべての実装は 1 回しか実行できません。
このような場合、実際には費用はかかりません foreach
ただし、一度もループしない可能性があるという事実を考慮に入れてください。
最初のメソッドには、Empty 拡張メソッドの単純な実装があります。http://signum.codeplex.com/SourceControl/changeset/view/25903#510468