降伏リターン説明!


yieldキーワードは、常に非常に誤解されたキーワードですC# , だから私は簡単な例ので、うまくいけば、それはあなたのアプリケーションのためのより良いパフォーマンスを得るのを助けるように理解できるように説明しようとします.

➽コンソールアプリの作成


☞まず最初に、私たちには単純な.Net Core 3.1 学生のリストを処理するコンソールアプリケーション.Student Class には以下の構造体がある:
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
単純右のようですか?心配しないで、それはそのように保たれます😉
☞次の手順では、学生のリストを作成しますmain コンソールアプリケーションのメソッド
        static void Main(string[] args)
        {
            CreateStudents();
        }
☞だから私たちは単純な関数をCreateStudents これは1つのパラメータ(作成する学生の数)を取り、この関数は、学生を作成し、もしstudent ID が1000未満の場合、画面に入力されます.
public static void CreateStudents()
{
    var students = GetStudents(1000000);

    foreach (var student in students)
    {
        if (student.Id < 1000)
            Console.WriteLine($"Id: {student.Id}, Name: {student.Name}");
        else
            break;
    }
}

static IEnumerable<Student> GetStudents(int count)
{
    var students = new List<Student>();
    for (int i = 0; i < count; i++)
    {
        students.Add(new Student() { Id = i, Name = $"Student Number {i}" });
    }
    return students;
}
GetStudents 関数は、新しい学生リストを作成し、新しい学生とそれを記入し、それを返します.
☞アプリケーションを実行し、どのように動作し、もう一度私たちは1000未満のIDを持つ学生に入力したい覚えてみましょう.

☞ブレークポイントを設定する🔴 アフターコールGetStudents 関数は、100万人の学生が作成されることに注意してくださいforeach ループは最初の1000のみを印刷するために入力されますので、我々は本当に1000😖 ?
もちろん..
➥我々が欲しいものは、100万の記録をつくって、リストに彼らを加えて、完全に100万の記録で水和されるそのリストを返すのを防ぐことになっています.

➽適用収益リターン


では、いくつかの修正を適用しましょうCreateStudents 機能.
static IEnumerable<Student> GetStudents(int count)
{
    for (int i = 0; i < count; i++)
    {
        yield return new Student() { Id = i, Name = $"Student Number {i}" };
    }
}
☞のは、同じブレークポイントを設定すると、アプリケーションを実行して何が起こるかを参照してください.

☞我々は、現在foreach ループゼロ学生と作成!
☞別のブレークポイントを設定しましょうyield return ラインインサイドGetStudents 関数は、実際に何が起こっているかを見て、Visual StudioのステップF 10を押してデバッグプロセスを継続します.
☞デバッガは、学生の中に行きますforeach その後、キーワードの上にループしてreturn yield ラインインGetStudents 関数をステップインしないforeach インCreateStudents 機能.

☞その後、我々は内部ですforeach 次に、if文を入力し、最初の学生のデータをコンソールに印刷します.
☞F 10を使用してステップを続行します.あなたのキーワードをヒットします**in**foreach それから、return yield 再び.

➽それで、我々は本当にここで何をしていますか?


☞実際に私たちは怠惰に学生のコレクションを繰り返している、我々は一度に1つの学生を要求しています.
☞両方の方法で我々は同じような結果を得ている

☞それらの違いはyield return パフォーマンスとメモリ使用量の巨大な改善を行います.
☞実際には、基本的にイテレータを作成します.
我々foreach ループは、このような声明のための構文的な砂糖です
var studentesEnumerator = students.GetEnumerator();
while (studentesEnumerator.MoveNext())
{
    var student = studentesEnumerator.Current;
}
これは大まかにコンパイルするものの種類です.
➥だから、全体のコレクションを繰り返す必要がないことによって、我々は実際にリスト内の100万人の学生を作成する必要はありませんでした、我々は実際にループし、最初の1000年を作成しました
使用するからyield 私たちがこのループに入ったとき、生徒は一度に一つずつ創造します.

➽ベンチマークDOTNETを使用して差分をテストします。


● 今、テストし、それが本当に違いを作っているかどうかを確認するアプリケーションのパフォーマンスを診断しましょう.
● このテストでは、ベンチマークネットNuGetパッケージを使用して、ここからダウンロードできますBenchmarkDotNet Nuget
● 我々は、単にベンチマーク統計を適用できるように我々のコードに簡単な編集を行います.
● 我々はクラスを作成し、それを呼び出しましょうYieldBenchmarks , このクラスには、古いバージョンの関数とyield returnを使用した新しいバージョンが含まれます.このクラスは、[ MemoryManager ]属性とCreateStudent 関数は[ベンチマーク]属性で修飾され、コードは次のようになります
    [MemoryDiagnoser]
    public class YieldBenchmarks
    {
        [Benchmark]
        public void CreateStudents()
        {
            var students = GetStudents(1000000);

            foreach (var student in students)
            {
                if (student.Id < 1000)
                    Console.WriteLine($"Id: {student.Id}, Name: {student.Name}");
                else
                    break;
            }
        }

        static IEnumerable<Student> GetStudents(int count)
        {
            var students = new List<Student>();
            for (int i = 0; i < count; i++)
            {
                students.Add(new Student() { Id = i, Name = $"Student Number {i}" });
            }
            return students;
        }

        [Benchmark]
        public void CreateStudentsYield()
        {
            var students = GetStudentsYield(1000000);

            foreach (var student in students)
            {
                if (student.Id < 1000)
                    Console.WriteLine($"Id: {student.Id}, Name: {student.Name}");
                else
                    break;
            }
        }

        static IEnumerable<Student> GetStudentsYield(int count)
        {
            for (int i = 0; i < count; i++)
            {
                yield return new Student() { Id = i, Name = $"Student Number {i}" };
            }
        }
    }
main 関数は以下のようになります:
        static void Main(string[] args)
        {
            var result = BenchmarkRunner.Run<YieldBenchmarks>();
        }
● では、リリースモードでアプリケーションを実行しましょう.
● 注意BenchmarkRunner 各関数を複数回実行して、その診断を実行できるようになります.

● 今の経過時間の大きな違いに注意しても、メモリ使用量の巨大な違いに気付き、それは133.5 MB対220 KBです!
これは、単純な明確化のためのyield キーワード、次に、async yieldで行きます.待ちます:)