카드의 유효성 검사 오류를 얻는 다음 기능이 있습니다. 내 질문은 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_
발췌 문장을 생각해 냈습니다 .
다음은 스 니펫 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>
은 지연 평가를 허용하고 반환 은 허용 하지 않음). - 시퀀스는 무한 할 수 있으므로 반환해야 할 요소 수를 결정하기 위해 호출자에게 맡깁니다.