カードの検証エラーを取得するには、次の機能があります。私の質問は、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!
_と1つのアイテムに対してyield
でサポートしているものです。 (これは、末尾再帰に関して非常に便利です...)
残念ながら、C#ではサポートされていません。
ただし、それぞれ_IEnumerable<ErrorInfo>
_を返す複数のメソッドがある場合は、_Enumerable.Concat
_を使用してコードを単純化できます。
_private static IEnumerable<ErrorInfo> GetErrors(Card card)
{
return GetMoreErrors(card).Concat(GetOtherErrors())
.Concat(GetValidationErrors())
.Concat(AnyMoreErrors())
.Concat(ICantBelieveHowManyErrorsYouHave());
}
_
ただし、2つの実装には非常に重要な違いが1つあります。この実装は、返される反復子を1つずつしか使用しない場合でも、すべてのメソッドimmediatelyを呼び出します。既存のコードは、次のエラーについてasksになる前に、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は、呼び出されるたびに最終的な列挙で要素を返すと考えてください。そのため、そのようなforeachループで要素を呼び出すと、呼び出されるたびに1つの要素が返されます。 foreachに条件ステートメントを配置して、結果セットをフィルターする機能があります。 (単純に除外基準を放棄しないことにより)
後続のyieldをメソッドの後半で追加すると、列挙に1つの要素が追加され続け、次のようなことができるようになります...
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>>
このコードに実行の遅延を保持させる。私は多くの理由で遅延実行のファンです。その理由の1つは、膨大な数の列挙可能オブジェクトでもメモリフットプリントが小さいことです。
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>
を返すことはできません)。