Метод не вызывается при использовании возврата доходности - PullRequest
8 голосов
/ 16 апреля 2010

У меня небольшие проблемы с методом, в котором я использую yield return, это не работает ...

public IEnumerable<MyClass> SomeMethod(int aParam)
{
    foreach(DataRow row in GetClassesFromDB(aParam).Rows)
    {
        yield return new MyClass((int)row["Id"], (string)row["SomeString"]);
    }    
}

Приведенный выше код никогда не запускается, когда вызов этого метода просто перешагивает через него.

Однако, если я перейду на ...

public IEnumerable<MyClass> SomeMethod(int aParam)
{
    IList<MyClass> classes = new List<MyClass>();

    foreach(DataRow row in GetClassesFromDB(aParam).Rows)
    {
         classes.Add(new MyClass((int)rows["Id"], (string)row["SomeString"]);
    }

    return classes;
}

Работает просто отлично.

Я не понимаю, почему первый метод никогда не запускается, не могли бы вы помочь мне понять, что здесь происходит?

Ответы [ 3 ]

8 голосов
/ 16 апреля 2010

Версия yield дает "run", только когда вызывающая сторона фактически начинает перечислять возвращенную коллекцию.

Если, например, вы получаете только коллекцию:

var results = SomeObject.SomeMethod (5);

и ничего с этим не делать, SomeMethod не будет выполняться.

Только когда вы начнете перечислять коллекцию results, она ударит.

foreach (MyClass c in results)
{
    /* Now it strikes */
}
2 голосов
/ 16 апреля 2010
Методы

yield return фактически преобразуются в классы конечных автоматов, которые лениво извлекают информацию - только когда вы фактически запрашиваете ее. Это означает, что для фактического извлечения данных необходимо выполнить итерацию по результату вашего метода.

// Gives you an iterator object that hasn't done anything yet
IEnumerable<MyClass> list = SomeMethod(); 

// Enumerate over the object
foreach (var item in list ) {
  // Only here will the data be retrieved. 
  // The method will stop on yield return every time the foreach loops.
}

Причина, по которой он запускается во втором случае, заключается в том, что блока доходности нет, и, следовательно, весь метод выполняется за один раз.

В этом конкретном случае маловероятно, что у вас будет какое-либо преимущество в использовании блока итератора по сравнению с обычным, потому что ваш GetClassesFromDb() тоже не один. Это означает, что он будет извлекать все данные одновременно при первом запуске. Блоки итераторов лучше всего использовать, когда вы можете обращаться к элементам по одному, потому что таким образом вы можете остановиться, если они вам больше не нужны.

0 голосов
/ 18 марта 2017

Мне пришлось почти катастрофически узнать, насколько круто / опасно yield, когда я решил заставить анализатор нашей компании лениво читать входящие данные. К счастью, только одна из немногих наших реализующих функций фактически использовала ключевое слово yield. Потребовалось несколько дней, чтобы понять, что он тихо вообще ничего не делал.

Ключевое слово yield будет настолько ленивым, насколько это возможно, в том числе вообще пропустит метод, если вы не включите его для работы с чем-то вроде .ToList() или .FirstOrDefault() или .Any()

Ниже представлены две вариации: одна с использованием ключевого слова, а вторая с возвратом прямого списка. Один даже не потрудится выполнить, а другой выполнит, даже если они кажутся одинаковыми.

public class WhatDoesYieldDo
{
    public List<string> YieldTestResults;

    public List<string> ListTestResults;

    [TestMethod]
    public void TestMethod1()
    {
        ListTest();
        Assert.IsTrue(ListTestResults.Any());

        YieldTest();
        Assert.IsTrue(YieldTestResults.Any());
    }

    public IEnumerable<string> YieldTest()
    {
        YieldTestResults = new List<string>();
        for (var i = 0; i < 10; i++)
        {
            YieldTestResults.Add(i.ToString(CultureInfo.InvariantCulture));
            yield return i.ToString(CultureInfo.InvariantCulture);
        }
    }

    public IEnumerable<string> ListTest()
    {
        ListTestResults = new List<string>();

        for (var i = 0; i < 10; i++)
        {
            ListTestResults.Add(i.ToString(CultureInfo.InvariantCulture));
        }

        return ListTestResults;
    }
}

Мораль истории: убедитесь, что если у вас есть метод, который возвращает IEnumerable, и вы используете yield в этом методе, у вас есть что-то, что будет перебирать результаты, или метод вообще не будет выполняться. *

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...