Интересное использование ключевого слова 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)
,
Помимо этого, у меня есть несколько вопросов:
Когда делаются условные части
if
заявления оцениваются? когдаGetRuleViolations()
называется или когда перечислимое на самом деле повторяется? Другими словами, если значениеTitle
изменения отnull
вReally Geeky Dinner
между тем временем, когда я звонюGetRuleViolations()
и время, когда я попытаюсь это сделать, будетRuleViolation("Title required", "Title")
быть созданным или нет?Почему
yield break;
необходимо? Что здесь на самом деле делает?Скажем
Title
является нулевым или пустым. Если я позвонюGetRuleViolations()
затем перебрать полученное перечисление два раза подряд, сколько раз будетnew RuleViolation("Title required", "Title")
называться?
3 ответа
Функция, которая содержит yield
Команды обрабатываются иначе, чем обычная функция. Когда эта функция вызывается, происходит за кулисами то, что анонимный тип состоит из IEnumerable
Тип функции, функция создает объект этого типа и возвращает его. Анонимный класс содержит логику, которая выполняет тело функции до следующего yield
команда для каждого раза IEnumerable.MoveNext
называется. Это немного вводит в заблуждение: тело функции выполняется не в одном пакете, как обычная функция, а по частям, каждый фрагмент выполняется, когда перечислитель перемещается на один шаг вперед.
Что касается ваших вопросов:
- Как я уже сказал, каждый
if
выполняется, когда вы переходите к следующему элементу. yield break
действительно не нужно в приведенном выше примере. Что он делает, так это прекращает перечисление.- Каждый раз, когда вы перебираете перечислимое значение, вы снова запускаете код. Поставьте точку останова на соответствующей строке и проверьте сами.
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: каждый раз. Конечно, вы можете кэшировать результат, превратив его в список, например, и повторив его потом.