コードインスペクション:複数列挙の可能性
次のコードスニペットを考えてみましょう。
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() がデータベースクエリの場合、さらに悪化する可能性があります。その場合、他のプロセスがその間にデータベースを変更すると、 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
}
この場合、ReSharper はほとんどの場合にそのメソッドが追加の列挙を行うと想定します。 メソッドが実際に 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 日