При изучении вариантов создания пользовательской коллекции встретил фразу, что для реализации итератора можно использовать yield, и оно действительно работает, но я так и не могу понять, что именно делает оператор.
Как и было написано, он автоматически с генерирует код энумератора и предоставит возможность с помощью цикла foreach перебрать все предоставленные через yield элементы.
например есть следующий код:
class MyCollection : IEnumerable
{
public IEnumerator GetEnumerator()
{
yield return "Hello world!";
yield return "Hello world!1";
yield return "Hello world!2";
yield return "Hello world!3";
}
}
Что бы вручную не реализовывать интерфейс IEnumerable был использован yield через который и были переданы необходимые объекты для перебора. Если вскрыть данную часть кода через dotPeek, то можно увидеть сгенерированный компилятор вложенный класс (за его создание отвечал yield):
class MyCollection : IEnumerable
{
public static IEnumerable GetEnumerator()
{
return new ClassGetEnumerator(-2);
}
private sealed class ClassGetEnumerator : IEnumerable<object>, IEnumerator<object>, IEnumerator, IDisposable
{
private int state;
private object current;
private int initialThreadId;
public ClassGetEnumerator(int state)
{
this.state = state;
this.initialThreadId = Thread.CurrentThread.ManagedThreadId;
}
//private bool IEnumerator.MoveNext() // Так в Рефлекторе
bool IEnumerator.MoveNext()
{
switch (this.state)
{
case 0:
this.state = -1;
this.current = "Hello world!";
this.state = 1;
return true;
case 1:
this.state = -1;
this.current = "Hello world!1";
this.state = 2;
return true;
case 2:
this.state = -1;
this.current = "Hello world!2";
this.state = 3;
return true;
case 3:
this.state = -1;
this.current = "Hello world!3";
this.state = 1;
return true;
case 4:
this.state = -1;
break;
}
return false;
}
IEnumerator<object> IEnumerable<object>.GetEnumerator()
{
if ((Thread.CurrentThread.ManagedThreadId == this.initialThreadId) && (this.state == -2))
{
this.state = 0;
return this;
}
return new UserCollection.ClassGetEnumerator(0);
}
IEnumerator IEnumerable.GetEnumerator()
{
// Так в Рефлекторе
//return this.System.Collections.Generic.IEnumerable<System.Object>.GetEnumerator();
return (this as IEnumerable<object>).GetEnumerator();
}
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
void IDisposable.Dispose()
{
}
object IEnumerator<object>.Current
{
get
{
return this.current;
}
}
object IEnumerator.Current
{
get
{
return this.current;
}
}
}
}
Таким образом с помощью yield можно создавать различные энумераторы. В случае если нужно прекратить передачу энумератору (например, по достижении определённого количества элементов), то можно использовать yield break;.
Советую создать пару энумераторов с помощью yield , после Ваш же код вскрыть через какой-то рефлектор и разобраться с сгенерированным кодом.
может посоветуете какую-то статью/видео, где более детально рассматривается данный оператор?
http://sergeyteplyakov.blogspot.com/2010/06/c-1.html (статья состоит из трёх частей, смотрите внимательно)
https://metanit.com/sharp/tutorial/4.12.php - просто описание оператора yield не рассматривая его технической реализации
https://itvdn.com/ru/video/csharp-essential/collections - с помощью этого урока изучал работу yield я, но доступ у нему откроется только после подписки
Спасибо большое!