Отож головне що треба про нього знати так це те що код типу:
1: private static IEnumerable<string> GetString()
2: {
3: yield return "X";
4: yield return "XX";
5: yield return "XXX";
6: }
фактично перетвориться в обєкт який наслідує
1: private sealed class <GetInt> d__0 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IEnumerator, IDisposable
і має дуже хитру реалізацію методу MoveNext()
Фішка в тому що цей метод містить такий код:
1: bool IEnumerator.MoveNext()
2: {
3: switch (this.<>1__state)
4: {
5: case 0:
6: this.<>1__state = -1;
7: this.<>2__current = "X";
8: this.<>1__state = 1;
9: return true;
10: case 1:
11: this.<>1__state = -1;
12: this.<>2__current = "XX";
13: this.<>1__state = 2;
14: return true;
15: case 2:
16: this.<>1__state = -1;
17: this.<>2__current = "XXX";
18: this.<>1__state = 3;
19: return true;
20: case 3:
21: this.<>1__state = -1;
22: break;
23: }
24: return false;
25: }
Як бачимо при кожному виклику методу ми в 2__current записуємо необхідні нам дані та змінну яка відповідає за стан змінюємо так щоб вона вказувала на наступний кейс в свічі. Така собі простенька стейт машина.
Вигода стає явною коли нам треба сформувати специфічну колекцію в якій елементи будуть залежати від певних обставин(пора року, тип поточно юзера…)
Наприклад:
1: private static IEnumerable<string> GetString()
2: {
3: yield return "Like";
4: if (role == "admin")
5: {
6: yield return "Remove";
7: }
8: else
9: {
10: yield return "Spam";
11: }
12: yield return "Ignore";
13: }
В даному випадку якщо на основі колекції формується меню то для юзера буде доступно поскаржитись на спам а для адміна зразу видалити запис. Приклад хоч і на думаний але дає зрозуміти суть.
в результаті ми отримаємо:
1: bool IEnumerator.MoveNext()
2: {
3: switch (this.<>1__state)
4: {
5: case 0:
6: this.<>1__state = -1;
7: this.<>2__current = "Like";
8: this.<>1__state = 1;
9: return true;
10: case 1:
11: this.<>1__state = -1;
12: if (Program.role == "admin")
13: {
14: this.<>2__current = "Remove";
15: this.<>1__state = 2;
16: return true;
17: }
18: else
19: {
20: this.<>2__current = "Spam";
21: this.<>1__state = 3;
22: return true;
23: }
24: case 2:
25: this.<>1__state = -1;
26: break;
27: case 3:
28: this.<>1__state = -1;
29: break;
30: case 4:
31: this.<>1__state = -1;
32: goto default;
33: default:
34: return false;
35: }
36: this.<>2__current = "Ignore";
37: this.<>1__state = 4;
38: return true;
39: }
Плюс до всього вище описаного, треба зрозуміти, що кожен мувнекст переходить на певний case і відповідно код який є в цьому case виконається лише тоді, коли ми дойдемо до нього, таким чином якщо ми б створювали якісь обєкти в одному з yield return то фактично отримуємо “ліниве завантаження” і відповідно програма працювала б ефективніше …
І останній приклад
1:
2: public static IEnumerable<int> GetInt()
3: {
4: for (int i = 0; i < 5; i++)
5: {
6: yield return i;
7:
8: }
9: }
в результаті дасть нам :
1: bool IEnumerator.MoveNext()
2: {
3: switch (this.<>1__state)
4: {
5: case 0:
6: this.<>1__state = -1;
7: this.<i>5__1 = 0;
8: break;
9: case 1:
10: this.<>1__state = -1;
11: ++this.<i>5__1;
12: break;
13: default:
14: return false;
15: }
16: if (this.<i>5__1 < 5)
17: {
18: this.<>2__current = this.<i>5__1;
19: this.<>1__state = 1;
20: return true;
21: }
22: else
23: goto default;
24: }
Отож підсумок, yield дає можливість модифікувати логіку методу MoveNext що є неможливим при роботі з простими колекціями. Те ж саме можна зробити реалізувавши всі необхідні інтерфейси вручну але це набагато довше.
Комментариев нет:
Отправить комментарий