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
그들을 통해 열거하지 않고 모든 오류를 반환 할 수 있습니까?
그것에 대해 생각하면 이것은 어리석은 질문 일 것입니다. 그러나 내가 잘못되지 않도록하고 싶습니다.
확실히 바보 같은 질문은 아니며 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_
발췌 문장을 생각해 냈습니다 .
Here's the snippet 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>
I don't see anything wrong with your function, I'd say that it is doing what you want.
Think of the Yield as returning an element in the final Enumeration each time that it is invoked, so when you have it in the foreach loop like that, each time it is invoked it returns 1 element. You have the ability to put conditional statements in your foreach to filter the resultset. (simply by not yielding on your exclusion criteria)
If you add subsequent yields later in the method, it will continue to add 1 element to the enumeration, making it possible to do things like...
public IEnumerable<string> ConcatLists(params IEnumerable<string>[] lists)
{
foreach (IEnumerable<string> list in lists)
{
foreach (string s in list)
{
yield return s;
}
}
}
Yes it is possible to return all errors at once. Just return a List<T>
or ReadOnlyCollection<T>
.
By returning an IEnumerable<T>
you're returning a sequence of something. On the surface that may seem identical to returning the collection, but there are a number of difference, you should keep in mind.
Collections
- The caller can be sure that both the collection and all the items will exist when the collection is returned. If the collection must be created per call, returning a collection is a really bad idea.
- Most collections can be modified when returned.
- The collection is of finite size.
Sequences
- Can be enumerated - and that is pretty much all we can say for sure.
- A returned sequence itself cannot be modified.
- Each element may be created as part of running through the sequence (i.e. returning
IEnumerable<T>
allows for lazy evaluation, returningList<T>
does not). - 시퀀스는 무한 할 수 있으므로 반환해야 할 요소 수를 결정하기 위해 호출자에게 맡깁니다.
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
수 있습니다.
참고 URL : https://stackoverflow.com/questions/1270024/nested-yield-return-with-ienumerable
'Programing' 카테고리의 다른 글
JSON은 Infinity와 NaN을 제외했습니다. (0) | 2020.06.06 |
---|---|
비밀번호 소금에 절인 : 모범 사례? (0) | 2020.06.06 |
파이썬 'for'루프에서 범위 지정 (0) | 2020.06.06 |
git 저장소에 여러 프로젝트를 배치하는 가장 좋은 방법은 무엇입니까? (0) | 2020.06.06 |
역 반복자를 사용하여 지우기를 호출하는 방법 (0) | 2020.06.06 |