C# Что делает yield?


#1

При изучении вариантов создания пользовательской коллекции встретил фразу, что для реализации итератора можно использовать yield, и оно действительно работает, но я так и не могу понять, что именно делает оператор.


#2

Как и было написано, он автоматически с генерирует код энумератора и предоставит возможность с помощью цикла 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 , после Ваш же код вскрыть через какой-то рефлектор и разобраться с сгенерированным кодом.


#3

может посоветуете какую-то статью/видео, где более детально рассматривается данный оператор?


#4

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 я, но доступ у нему откроется только после подписки


#5

Спасибо большое!