모든 열거 형을 한 번에 수율로 반환합니다. 반복하지 않고 메소드 모두

카드의 유효성 검사 오류를 얻는 다음 기능이 있습니다. 내 질문은 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를 열거 할 필요없이 반환 할 수 있습니까?

그것에 대해 생각하면 이것은 어리석은 질문 일지 모르지만, 내가 잘못되지 않도록하고 싶습니다.



답변

확실히 바보 같은 질문은 아니며 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() 전에 모든 것을 반복 할 때까지 기다립니다 .

일반적으로 이것은 중요하지 않지만 언제 어떤 일이 일어날 지 이해하는 것이 좋습니다.


답변

이와 같은 모든 오류 소스를 설정할 수 있습니다 (Jon Skeet의 답변에서 빌린 메소드 이름).

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도 지연됩니다.


답변

나는 빠른 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>


답변

나는 당신의 기능에 문제가 없다고 생각합니다. 원하는대로하고 있다고 말하고 싶습니다.

Yield는 호출 될 때마다 최종 Enumeration에서 요소를 리턴하는 것으로 생각하므로, foreach 루프에 해당 요소가있을 때마다 호출 될 때마다 1 개의 요소를 리턴합니다. 결과 집합을 필터링하기 위해 foreach에 조건문을 넣을 수 있습니다. (단지 제외 기준을 제시하지 않음으로써)

나중에 메소드에서 후속 수확량을 추가하면 열거 형에 하나의 요소가 계속 추가되어 다음과 같은 작업을 수행 할 수 있습니다.

public IEnumerable<string> ConcatLists(params IEnumerable<string>[] lists)
{
  foreach (IEnumerable<string> list in lists)
  {
    foreach (string s in list)
    {
      yield return s;
    }
  }
}


답변

IEnumerable<IEnumerable<T>>아무도이 코드가 지연 된 실행을 유지하도록 간단한 확장 메소드를 권장하지 않는다고 생각합니다 . 나는 여러 가지 이유로 지연된 실행의 팬입니다. 그중 하나는 거대한 맹수 열거 형이라도 메모리 풋 프린트가 작다는 것입니다.

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로 이동할 UnWrap수 있습니다.


답변

예, 모든 오류를 한 번에 반환 할 수 있습니다. 그냥 List<T>또는ReadOnlyCollection<T> .

반환하여 IEnumerable<T> 일련의 무언가가 반환됩니다. 컬렉션을 반환하는 것과 동일하게 보일 수 있지만 여러 가지 차이점이 있으므로 염두에 두어야합니다.

컬렉션

  • 호출자는 컬렉션이 반환 될 때 컬렉션과 모든 항목이 모두 존재하는지 확인할 수 있습니다. 호출마다 컬렉션을 만들어야하는 경우 컬렉션을 반환하는 것은 정말 나쁜 생각입니다.
  • 대부분의 컬렉션은 반환 될 때 수정할 수 있습니다.
  • 컬렉션은 유한 한 크기입니다.

시퀀스

  • 열거 될 수 있습니다-그것은 우리가 확실히 말할 수있는 거의 모든 것입니다.
  • 반환 된 시퀀스 자체는 수정할 수 없습니다.
  • 각 요소는 시퀀스를 통한 실행의 일부로 생성 될 수 있습니다 (즉 IEnumerable<T>, 반환 List<T>은 지연 평가를 허용하고 반환 은 허용 하지 않음).
  • 시퀀스는 무한 할 수 있으므로 반환해야 할 요소 수를 결정하기 위해 호출자에게 맡깁니다.

답변