web-dev-qa-db-ja.com

C#:foreach内のyield returnが失敗する-本体をイテレーターブロックにすることはできません

この難読化されたコードについて考えてみましょう。その目的は、匿名コンストラクターとそのオブジェクトをyield returnを介してその場で新しいオブジェクトを作成することです。目的は、単にreturnするだけでローカルコレクションを維持する必要がないようにすることです。

public static List<DesktopComputer> BuildComputerAssets()
{           
    List<string> idTags = GetComputerIdTags();

    foreach (var pcTag in idTags)
    {
        yield return new DesktopComputer() {AssetTag= pcTag
                                          , Description = "PC " + pcTag
                                          , AcquireDate = DateTime.Now
                                           };
    }            
}

残念ながら、このコードでは例外が発生します。

エラー28 'System.Collections.Generic.List'はイテレーターインターフェイスタイプではないため、 'Foo.BuildComputerAssets()'の本体をイテレーターブロックにすることはできません

質問

  • このエラーメッセージはどういう意味ですか?
  • このエラーを回避し、yield returnを適切に使用するにはどうすればよいですか?
42
p.campbell

yield returnではなくIEnumerableまたはIEnumeratorを返す関数でのみList<T>を使用できます。

IEnumerable<DesktopComputer>を返すように関数を変更する必要があります。

または、関数を書き換えて List<T>.ConvertAll を使用することもできます。

return GetComputerIdTags().ConvertAll(pcTag => 
    new DesktopComputer() {
        AssetTag    = pcTag,
        Description = "PC " + pcTag,
        AcquireDate = DateTime.Now
    });
53
SLaks

メソッドの署名が間違っています。そのはず:

public static IEnumerable<DesktopComputer> BuildComputerAssets()
17
Brian Genisio

yield は、イテレータータイプでのみ機能します。

収量ステートメントは、イテレーターブロック内にのみ表示できます

イテレータ は次のように定義されます

イテレーターの戻りタイプは、IEnumerable、IEnumerator、IEnumerable <T>、またはIEnumerator <T>でなければなりません。

IListとIList <T>はIEnumerable/IEnumerable <T>を実装しますが、列挙子へのすべての呼び出し元は、上記の4つのタイプのいずれかを期待し、それ以外は期待しません。

8
Michael Stum

LINQクエリを使用して同じ機能を実装することもできます(C#3.0以降)。これはConvertAllメソッドを使用するよりも効率的ではありませんが、より一般的です。後で、フィルタリングなどの他のLINQ機能を使用する必要がある場合もあります。

return (from pcTag in GetComputerIdTags()
        select new DesktopComputer() { 
          AssetTag    = pcTag, 
          Description = "PC " + pcTag, 
          AcquireDate = DateTime.Now 
        }).ToList();

ToListメソッドは、結果をIEnumerable<T>からList<T>に変換します。個人的にはConvertAllは好きではありません。LINQと同じことをするからです。ただし、以前に追加されたため、LINQでは使用できません(Selectと呼ばれているはずです)。

2
Tomas Petricek