kzen.dev
  • Вопросы
  • Метки
  • Пользователи
Оповещения
Вознаграждения
Регистрация
После регистрации, сможете получать уведомления об ответах и комментариях на Ваши вопросы.
Вход
Если у Вас уже есть аккаунт, войдите чтобы проверить новые уведомления.
Тут будут вознаграждения за добавленные вопросы, ответы и комментарий.
Дополнительно
Источник
Редактировать
John Oxley
John Oxley
Вопрос

Вложенный возврат доходности с помощью IEnumerable

У меня есть следующая функция для получения ошибок валидации для карточки. Мой вопрос связан с работой с GetErrors. Оба метода имеют одинаковый возвращаемый тип IEnumerable<ErrorInfo>.

private static IEnumerable<ErrorInfo> GetErrors(Card card)
{
    var errors = GetMoreErrors(card);
    foreach (var e in errors)
        yield return e;

    // further yield returns for more validation errors
}

Возможно ли вернуть все ошибки в GetMoreErrors без необходимости их перечисления?

Думаю, это, вероятно, глупый вопрос, но я хочу убедиться, что не ошибаюсь.

157 2009-08-13T04:26:40+00:00 6
John Oxley
John Oxley
Редактировал вопрос 13-го августа 2009 в 9:09
Программирование
c#
ienumerable
yield
yield-return
Решение / Ответ
Jon Skeet
Jon Skeet
13-го августа 2009 в 5:30
2009-08-13T05:30:06+00:00
Дополнительно
Источник
Редактировать
#9365655

Это определенно не глупый вопрос, и это то, что F# поддерживает с yield! для целой коллекции против yield для отдельного элемента. (Это может быть очень полезно в плане хвостовой рекурсии...)

К сожалению, это не поддерживается в C#.

Однако, если у вас есть несколько методов, каждый из которых возвращает IEnumerable<ErrorInfo>, вы можете использовать Enumerable.Concat для упрощения кода:

private static IEnumerable<ErrorInfo> GetErrors(Card card)
{
    return GetMoreErrors(card).Concat(GetOtherErrors())
                              .Concat(GetValidationErrors())
                              .Concat(AnyMoreErrors())
                              .Concat(ICantBelieveHowManyErrorsYouHave());
}

Однако между этими двумя реализациями есть одно очень важное различие: эта реализация будет вызывать все методы мгновенно, хотя она будет использовать возвращаемые итераторы только по одному за раз. Ваш существующий код будет ждать, пока он не просмотрит все в GetMoreErrors(), прежде чем он даже запросит о следующих ошибках.

Обычно это не так важно, но стоит понимать, что и когда будет происходить.

136
0
Adam Boddington
Adam Boddington
7-го апреля 2014 в 12:27
2014-04-07T12:27:32+00:00
Дополнительно
Источник
Редактировать
#9365657

Можно настроить все такой источники ошибок (имена методов, заимствованных из Джон Скит'ы ответ).

private static IEnumerable<IEnumerable<ErrorInfo>> GetErrorSources(Card card)
{
    yield return GetMoreErrors(card);
    yield return GetOtherErrors();
    yield return GetValidationErrors();
    yield return AnyMoreErrors();
    yield return ICantBelieveHowManyErrorsYouHave();
}

Тогда вы можете перебирать их в то же время.

private static IEnumerable<ErrorInfo> GetErrors(Card card)
{
    foreach (var errorSource in GetErrorSources(card))
        foreach (var error in errorSource)
            yield return error;
}

В качестве альтернативы вы могли бы расплющить источники ошибок с метода SelectMany.

private static IEnumerable<ErrorInfo> GetErrors(Card card)
{
    return GetErrorSources(card).SelectMany(e => e);
}

Выполнение методов в GetErrorSources будет слишком затягивается.

Adam Boddington
Adam Boddington
Редактировал ответ 7-го апреля 2014 в 12:33
22
0
John Gietzen
John Gietzen
14-го июня 2015 в 6:16
2015-06-14T18:16:17+00:00
Дополнительно
Источник
Редактировать
#9365658

Я придумал фрагмент быстрый yield_:

yield_ фрагментами анимации использование

Здесь'с XML-фрагмент:

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <Author>John Gietzen</Author>
      <Description>yield! expansion for C#</Description>
      <Shortcut>yield_</Shortcut>
      <Title>Yield All</Title>
      <SnippetTypes>
        <SnippetType>Expansion</SnippetType>
      </SnippetTypes>
    </Header>
    <Snippet>
      <Declarations>
        <Literal Editable="true">
          <Default>items</Default>
          <ID>items</ID>
        </Literal>
        <Literal Editable="true">
          <Default>i</Default>
          <ID>i</ID>
        </Literal>
      </Declarations>
      <Code Language="CSharp"><![CDATA[foreach (var $i$ in $items$) yield return $i$$end$;]]></Code>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>
15
0
Tim Jarvis
Tim Jarvis
13-го августа 2009 в 5:00
2009-08-13T05:00:20+00:00
Дополнительно
Источник
Редактировать
#9365654

Я не вижу ничего плохого в вашей функции, я бы сказал, что она делает то, что вы хотите.

Думайте о Yield как о возврате элемента в конечном перечислении каждый раз, когда она вызывается, поэтому, когда вы используете ее в цикле foreach, каждый раз, когда она вызывается, она возвращает 1 элемент. У вас есть возможность поместить условные операторы в цикл foreach для фильтрации набора результатов. (просто не выполняя критерии исключения).

Если вы добавите последующие выходы позже в методе, он будет продолжать добавлять 1 элемент в перечисление, что позволяет делать такие вещи, как...

public IEnumerable<string> ConcatLists(params IEnumerable<string>[] lists)
{
  foreach (IEnumerable<string> list in lists)
  {
    foreach (string s in list)
    {
      yield return s;
    }
  }
}
8
0
Brian Rasmussen
Brian Rasmussen
13-го августа 2009 в 5:38
2009-08-13T05:38:23+00:00
Дополнительно
Источник
Редактировать
#9365656

Да, можно вернуть все ошибки сразу. Просто верните List<T> или ReadOnlyCollection<T>.

Возвращая IEnumerable<T>, вы возвращаете последовательность чего-то. На первый взгляд это может показаться идентичным возврату коллекции, но есть ряд различий, о которых следует помнить.

Коллекции

  • Вызывающая сторона может быть уверена, что и коллекция, и все элементы будут существовать, когда коллекция будет возвращена. Если коллекция должна создаваться при каждом вызове, возврат коллекции - очень плохая идея.
  • Большинство коллекций могут быть изменены при возврате.
  • Коллекция имеет конечный размер.

Последовательности

  • Могут быть перечислены - и это практически все, что мы можем сказать наверняка.
  • Сама возвращаемая последовательность не может быть изменена.
  • Каждый элемент может быть создан в процессе выполнения последовательности (т.е. возвращение IEnumerable<T> позволяет ленивую оценку, а возвращение List<T> - нет).
  • Последовательность может быть бесконечной и, таким образом, оставить на усмотрение вызывающей стороны решение о том, сколько элементов должно быть возвращено.
Brian Rasmussen
Brian Rasmussen
Редактировал ответ 13-го августа 2009 в 6:07
3
0
Frank Bryce
Frank Bryce
22-го сентября 2016 в 2:20
2016-09-22T14:20:59+00:00
Дополнительно
Источник
Редактировать
#9365659

Я'м удивлены, никто не думал рекомендовать простой метод расширения на интерфейс IEnumerable<интерфейс IEnumerable<П>>` чтобы сделать этот код Сохранить отложенное выполнение. Я'м вентилятор отсрочки исполнения по многим причинам, одна из них заключается в том, что объем памяти невелик даже для огромных-mongous enumerables.

public static class EnumearbleExtensions
{
    public static IEnumerable<T> UnWrap<T>(this IEnumerable<IEnumerable<T>> list)
    {
        foreach(var innerList in list)
        {
            foreach(T item in innerList)
            {
                yield return item;
            }
        }
    }
}

И вы можете использовать его в вашем случае такой

private static IEnumerable<ErrorInfo> GetErrors(Card card)
{
    return DoGetErrors(card).UnWrap();
}

private static IEnumerable<IEnumerable<ErrorInfo>> DoGetErrors(Card card)
{
    yield return GetMoreErrors(card);

    // further yield returns for more validation errors
}

Кроме того, вы можете покончить с на функцию-обертку вокруг DoGetErrors и развернуть в callsite.

 huysentruitw
huysentruitw
Редактировал ответ 25-го октября 2016 в 9:17
3
0
Похожие сообщества 6
Microsoft Stack Jobs
Microsoft Stack Jobs
2 408 пользователей
Work & freelance only Microsoft Stack. Feed https://t.me/Microsoftstackjobsfeed Чат про F#: @Fsharp_chat Чат про C#: @CSharpChat Чат про Xamarin: @xamarin_russia Чат общения:@dotnettalks
Открыть telegram
С#
С#
2 330 пользователей
Стараемся не флудить. Пишем по делу. Правила: https://t.me/professorweb/430450 Для флуда @svoboda_obsh
Открыть telegram
CODE BLOG / C#
CODE BLOG / C#
1 763 пользователей
Чат для .NET разработчиков и C# программистов. По всем вопросам: @shwanoff Youtube-канал: https://youtube.com/codeblog Основной канал: @codeblog Вконтакте: https://vk.com/codeblog Правила: https://t.me/codeblog_csharp/246972 Вакансии по тегу #work
Открыть telegram
var chat = new Chat();
var chat = new Chat();
1 428 пользователей
Обсуждение вопросов по .NET Правила чата – https://blog.devdigest.today/chat-rules Чат для флуда – https://t.me/+zwxI91RGG6s2YzAy
Открыть telegram
C#/.NET Для Новичков
C#/.NET Для Новичков
293 пользователей
Группа создана для тех, кто изучает язык программирования C#. Верховный главнокомандующий: @BlackDeveloper Оффтоп - разрешен в меру, реклама - бан.
Открыть telegram
ext
ext
31 пользователей
Общение на темы YouTube канала и программирования. Вакансии не размещаем. Основной канал: @extremecode
Открыть telegram
Добавить вопрос
Категории
Все
Технологий
Культура / Отдых
Жизнь / Искусство
Наука
Профессии
Бизнес
Пользователи
Все
Новые
Популярные
1
Денис Васьков
Зарегистрирован 15 часов назад
2
Dima Patrushev
Зарегистрирован 2 дня назад
3
sirojidddin otaboyev
Зарегистрирован 1 неделю назад
4
Елена Гайдамамакинат
Зарегистрирован 1 неделю назад
5
Иван Степанюк
Зарегистрирован 1 неделю назад
ID
KO
RO
RU
© kzen.dev 2023
Источник
stackoverflow.com
под лицензией cc by-sa 3.0 с атрибуцией