Работая через учебное руководство (Профессионал ASP.NET MVC - Ужин Компьютерного фаната), я столкнулся с этим отрывком кода:
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")
позвониться?
Функция, которая содержит команды выхода
, обрабатывается иначе, чем обычная функция. То, что происходит за кулисами при вызове этой функции, это то, что анонимный тип построен из специфического IEnumerable
типа функции, функция создает объект этого типа и возвращает его. Класс анонимности содержит логику, которая выполняет тело функции до следующей команды yield
каждый раз при вызове IEnumerable.MoveNext
. Это немного вводит в заблуждение, тело функции выполняется не одной партией, как обычная функция, а фигурами, каждая фигура выполняется, когда перечислитель движется на шаг вперед.
Что касается ваших вопросов:
если
выполняется при итерации к следующему элементу.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) перерыв в доходности;
скажет IEnumerator
, что больше нечего перечислять.
3) один раз за перечисление.
Используя перерыв;
public IEnumerable<string> EnumerateUntilEmpty()
{
foreach (var name in nameList)
{
if (String.IsNullOrEmpty(name)) yield break;
yield return name;
}
}
Краткий вариант:
1: урожайность - это магическое ключевое слово "Остановить и вернуться позже", поэтому если высказывания перед "активным" были оценены.
2: перерыв урожайности явно завершает перечисление (думайте "перерыв" в случае переключения)
3: каждый раз. Конечно же, можно кэшировать результат, например, превратив его в список, а затем выполнить итерацию над ним
.