Интересное использование ключевого слова C# yield в учебнике Nerd Dinner

Работая с учебником (Professional ASP.NET MVC - Nerd Dinner), я наткнулся на следующий фрагмент кода:

public IEnumerable<RuleViolation> GetRuleViolations() {
    if (String.IsNullOrEmpty(Title))
        yield return new RuleViolation("Title required", "Title");
    if (String.IsNullOrEmpty(Description))
        yield return new RuleViolation("Description required","Description");
    if (String.IsNullOrEmpty(HostedBy))
        yield return new RuleViolation("HostedBy required", "HostedBy");
    if (String.IsNullOrEmpty(Address))
        yield return new RuleViolation("Address required", "Address");
    if (String.IsNullOrEmpty(Country))
        yield return new RuleViolation("Country required", "Country");
    if (String.IsNullOrEmpty(ContactPhone))
        yield return new RuleViolation("Phone# required", "ContactPhone");
    if (!PhoneValidator.IsValidNumber(ContactPhone, Country))
        yield return new RuleViolation("Phone# does not match country", "ContactPhone");
    yield break;
}

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

Этот пример немного странный для меня. Я думаю, что это задерживает создание каких-либо RuleViolation до тех пор, пока программист не запросит конкретный элемент в коллекции, используя for each или метод расширения LINQ, такой как .ElementAt(2),

Помимо этого, у меня есть несколько вопросов:

  1. Когда делаются условные части if заявления оцениваются? когда GetRuleViolations() называется или когда перечислимое на самом деле повторяется? Другими словами, если значение Title изменения от null в Really Geeky Dinner между тем временем, когда я звоню GetRuleViolations() и время, когда я попытаюсь это сделать, будет RuleViolation("Title required", "Title") быть созданным или нет?

  2. Почему yield break; необходимо? Что здесь на самом деле делает?

  3. Скажем Title является нулевым или пустым. Если я позвоню GetRuleViolations() затем перебрать полученное перечисление два раза подряд, сколько раз будет new RuleViolation("Title required", "Title") называться?

3 ответа

Решение

Функция, которая содержит yield Команды обрабатываются иначе, чем обычная функция. Когда эта функция вызывается, происходит за кулисами то, что анонимный тип состоит из IEnumerable Тип функции, функция создает объект этого типа и возвращает его. Анонимный класс содержит логику, которая выполняет тело функции до следующего yield команда для каждого раза IEnumerable.MoveNext называется. Это немного вводит в заблуждение: тело функции выполняется не в одном пакете, как обычная функция, а по частям, каждый фрагмент выполняется, когда перечислитель перемещается на один шаг вперед.

Что касается ваших вопросов:

  1. Как я уже сказал, каждый if выполняется, когда вы переходите к следующему элементу.
  2. yield break действительно не нужно в приведенном выше примере. Что он делает, так это прекращает перечисление.
  3. Каждый раз, когда вы перебираете перечислимое значение, вы снова запускаете код. Поставьте точку останова на соответствующей строке и проверьте сами.

1) Возьмите этот более простой пример:

public void Enumerate()
{
    foreach (var item in EnumerateItems())
    {
        Console.WriteLine(item);
    }
}

public IEnumerable<string> EnumerateItems()
{
    yield return "item1";
    yield return "item2";
    yield break;
}

Каждый раз, когда вы звоните MoveNext() от IEnumerator код возвращается из yield указать и перейти к следующей исполняемой строке кода.

2) yield break; скажет IEnumerator что больше нечего перечислять.

3) один раз на перечисление.

С помощью yield break;

public IEnumerable<string> EnumerateUntilEmpty()
{
    foreach (var name in nameList)
    {
        if (String.IsNullOrEmpty(name)) yield break;
        yield return name;
    }     
}

Укороченная версия:

1: Выход - волшебное ключевое слово "Остановись и вернись позже", поэтому операторы if перед "активным" были оценены.

2: yield break явно завершает перечисление (подумайте "break" в случае переключения)

3: каждый раз. Конечно, вы можете кэшировать результат, превратив его в список, например, и повторив его потом.

Другие вопросы по тегам