コードインスペクション:複数回の列挙の可能性
次のコードスニペットを考えてみましょう。
IEnumerable<string> names = GetNames();
foreach (var name in names)
Console.WriteLine("Found " + name);
var allNames = new StringBuilder();
foreach (var name in names)
allNames.Append(name + " ");
GetNames() が IEnumerable<string> を返すと仮定すると、2 つの foreach ステートメントでこのコレクションを 2 回列挙することで、実際に余分な作業をしています。
GetNames() がデータベースクエリになると、状況はさらに悪化する可能性があります。その場合、他のプロセスが 2 つの呼び出しの間にデータベースを変更した場合、両方の foreach ループで異なる値を取得することになるかもしれません。
この種の問題は簡単に解決できます。変数の初期化時にシーケンスを配列やリストに変換して列挙を強制します。例:
List<string> names = GetNames().ToList();
残りのコードは、配列型とリスト型の両方が IEnumerable インターフェースを実装しているため、同じままにすることができます。
偽陽性
場合によっては、このインスペクションは、IEnumerable オブジェクトが列挙される前にいくつかのメソッドに渡された場合に誤検出を引き起こすことがあります。 例えば:
public void Foo(IEnumerable<string> values)
{
ThrowIfNull(values, nameof(values));
var x = values.ToList(); // Possible multiple enumeration of IEnumerable
}
public static void ThrowIfNull<T>(T value, string name) where T : class
{
// custom check for null but no enumeration
}
この場合、JetBrains Rider はメソッドが追加の列挙を行うと仮定しますが、ほとんどの場合これは正しいです。 メソッドが実際に IEnumerable オブジェクトを列挙しない場合は、対応するパラメーターに [NoEnumerationAttribute] を指定して誤検出を防ぐことができます:
public void Foo(IEnumerable<string> values)
{
ThrowIfNull(values, nameof(values));
var x = values.ToList(); // No warnings about multiple enumeration
}
public static void ThrowIfNull<T>([NoEnumeration] T value, string name) where T : class
{
// custom check for null but no enumeration
}
2026 年 6 月 12 日