The_Unknown_Member Ответов: 2

C# как метод расширения take() работает с итераторами?


Привет. Я читал о том, как работают итераторы (доходность возвращается), и наткнулся на этот код

Это метод с доходностью доходности:
IEnumerable<int> GenerateWithYield() 
{ 
var i = 0; 
while (true) 
yield return ++i; 
} 


а это цикл foreach:
foreach(var number in GenerateWithYield().Take(5)) 
    Console.WriteLine(number);



Как же так .Взять() работу? Что он делает?

Я проверил MSDN и увидел в замечании, что этот метод Take использует отложенное выполнение. Отложенное выполнение запросов LINQ. так ведь? Я пытаюсь изучить LINQ, но сначала мне нужно понять результаты.

Что я уже пробовал:

Я проверил MSDN и увидел, что он использует отложенное выполнение. Но я еще не научился работать с LINQ, но для того, чтобы начать работать с ним, мне нужно сначала понять, как работают выходы.

#realJSOP

Этот тип обсуждения не подходит для раздела вопросов и ответов. Отнесите его на соответствующий форум.

2 Ответов

Рейтинг:
2

OriginalGriff

Начните с документации: Перечислимый.Take(TSource) Method (IEnumerable(TSource), Int32) (System.Linq)[^] и это объясняет, что он делает:

Цитата:
Этот метод реализуется с помощью отложенного выполнения. Немедленное возвращаемое значение - это объект, в котором хранится вся информация, необходимая для выполнения действия. Запрос, представленный этим методом, не выполняется до тех пор, пока объект не будет перечислен либо путем прямого вызова его метода GetEnumerator, либо с помощью foreach в Visual C# или для каждого из них в Visual Basic.

Take<tsource> перечисляет источник и выдает элементы до тех пор, пока не будет получено количество элементов или sourcecontains больше не будет элементов. Если count превышает количество элементов в исходном все элементы source возвращаются.

Если count меньше или равен нулю, источник не перечисляется и возвращается пустой IEnumerable<t>.


Если вы еще не понимаете Yield и Перечислители, то проигнорируйте то, что он говорит об отложенном выполнении, и предположите, что если у вас есть последовательность значений, он возвращает первые элементы вплоть до указанного вами количества: в этом случае он возвращает первые пять элементов.
int[] arr = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
foreach (int n in arr.Take(5))
   {
   Console.WriteLine(n);
   }
Произведет тот же эффект, что и
int[] arr = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] arrTemp = new int[5];
for (int i = 0; i < 5; i++)
   {
   arrTemp[i] = arr[i];
   }
foreach (int n in arrTemp)
   {
   Console.WriteLine(n);
   }
Все, что делает отложенное выполнение, - это позволяет системе не генерировать промежуточную последовательность, потому что она yield returns вместо этого каждый элемент по требованию.


Рейтинг:
1

F-ES Sitecore

Это, вероятно, поможет показать, как работают счетчики. Приведенный ниже код-это в основном то, что делает функция "Take", но когда вы расширяете ее и шагаете через нее строка за строкой в отладчике, вы получаете лучшее понимание того, что происходит

static IEnumerable<int> GenerateWithYield()
{
    var i = 0;
    while (true)
        yield return ++i;
}

static void Main(string[] args)
{
    IEnumerable<int> numbers = GenerateWithYield(); // your function isn't actually called here so it won't step into it
    // if GenerateWithYield was a function that didn't return an IEnumerable then it would have been called immediately

    IEnumerator<int> e = numbers.GetEnumerator();
            
    for(int i = 0; i < 3; i++)
    {
        if (e.MoveNext()) // this is when your function is actually called and the debugger will step into it
        {
            // the fact that GenerateWithYield is only executed when MoveNext is called is the
            // "deferred execution"
            // when MoveNext was called GenerateWithYield will run until "yield return" is hit
            int value = e.Current;
            // e.Current is whatever you returned in "yield return"
            // when GenerateWithYield hits yield return it goes into a "paused" state
            // this is different from "return" in a normal function where the function is stopped completely
            // When MoveNext is called again your function resumes and again runs until the next
            // yield return is hit at which point it pauses again

            // So a normal function is executed when you call it and it runs until a "return" and then stops
            // A function that returns IEnumerable is called when you use the IEnumerator and has a
            // "pause/resume" cycle where it pauses when it hits yield return and resumes when asked
            // for the next value
            Console.WriteLine(value);
        }
    }