14

チュートリアル (Professional ASP.NET MVC - Nerd Dinner) に取り組んでいると、次のコード スニペットに出会いました。

public IEnumerable<RuleViolation> GetRuleViolations() {
    if (String.IsNullOrEmpty(Title))
        yield return new RuleViolation("Title required", "Title");
    if (String.IsNullOrEmpty(Description))
        yield return new RuleViolation("Description required","Description");
    if (String.IsNullOrEmpty(HostedBy))
        yield return new RuleViolation("HostedBy required", "HostedBy");
    if (String.IsNullOrEmpty(Address))
        yield return new RuleViolation("Address required", "Address");
    if (String.IsNullOrEmpty(Country))
        yield return new RuleViolation("Country required", "Country");
    if (String.IsNullOrEmpty(ContactPhone))
        yield return new RuleViolation("Phone# required", "ContactPhone");
    if (!PhoneValidator.IsValidNumber(ContactPhone, Country))
        yield return new RuleViolation("Phone# does not match country", "ContactPhone");
    yield break;
}

を読みましたがyield、私の理解はまだ少しぼんやりしていると思います。それがしているように見えるのは、絶対に必要な場合を除き、実際に循環を行うことなく、コレクション内のアイテムを循環できるオブジェクトを作成することです。

ただし、この例は私には少し奇妙です。私が考えていることはRuleViolation、プログラマーがコレクション内の特定の項目を実際に要求するか、for eachまたは のような LINQ 拡張メソッドを使用するまで、インスタンスの作成を遅らせること.ElementAt(2)です。

ただし、これ以外にもいくつか質問があります。

  1. ifステートメントの条件部分はいつ評価されますか? いつGetRuleViolations()呼び出されるか、または列挙型が実際に反復されるのはいつですか? 言い換えれば、私が呼び出してから実際にそれを反復しようとするまでの間に から の値が変化した場合、作成さTitleれるかどうか?nullReally Geeky DinnerGetRuleViolations()RuleViolation("Title required", "Title")

  2. なぜyield break;必要なのですか?ここで実際に何をしているのですか?

  3. Titlenull または空であるとしましょう。呼び出しGetRuleViolations()てから、結果の列挙型を 2 回続けて反復すると、何回new RuleViolation("Title required", "Title")呼び出されますか?

4

3 に答える 3

18

コマンドを含む関数yieldは、通常の関数とは異なる方法で処理されます。その関数が呼び出されたときに舞台裏で起こっていることは、関数の特定のIEnumerable型で匿名型が構築され、関数がその型のオブジェクトを作成して返すことです。匿名クラスには、が呼び出されるyieldたびに次のコマンドまで関数の本体を実行するロジックが含まれています。IEnumerable.MoveNext少し誤解を招くかもしれませんが、関数の本体は通常の関数のように 1 つのバッチで実行されるのではなく、列挙子が 1 ステップ進むと、それぞれの部分が実行されます。

あなたの質問に関して:

  1. 私が言ったように、次の要素に反復すると、それぞれifが実行されます。
  2. yield break上記の例では実際には必要ありません。それが行うことは、列挙を終了することです。
  3. 列挙型を反復処理するたびに、コードの実行を再度強制します。関連する行にブレークポイントを置き、自分でテストしてください。
于 2009-12-28T19:30:19.953 に答える
6

1) 次の簡単な例を見てください。

public void Enumerate()
{
    foreach (var item in EnumerateItems())
    {
        Console.WriteLine(item);
    }
}

public IEnumerable<string> EnumerateItems()
{
    yield return "item1";
    yield return "item2";
    yield break;
}

MoveNext()コードから呼び出すたびに、ポイントIEnumeratorから戻り、次の実行可能なコード行に移動します。yield

2)列挙するものはこれ以上ないことを にyield break;伝えます。IEnumerator

3) 列挙ごとに 1 回。

使用するyield break;

public IEnumerable<string> EnumerateUntilEmpty()
{
    foreach (var name in nameList)
    {
        if (String.IsNullOrEmpty(name)) yield break;
        yield return name;
    }     
}
于 2009-12-28T19:29:09.857 に答える
2

短縮版:

1: yield は魔法の "Stop and come back later" キーワードであるため、"active" ステートメントの前の if ステートメントが評価されています。

2: yield break は列挙を明示的に終了します (switch の場合の「break」を考えてください)。

3: 毎回。もちろん、結果をたとえばリストに変換し、後でそれを反復処理することで、結果をキャッシュできます。

于 2009-12-28T19:31:51.380 に答える