5

静的拡張メソッドで列挙型を受け入れ、列挙型のコンテンツにファンクターを適用しながら、そのコンテンツを反復処理する必要がある流暢なビルダー パターンを実装しています。のように (実際のコードではなく、単なる図です):

public static IValidator<IEnumerable<T>> Each<T>(
    this IValidator<IEnumerable<T>> enumerable, 
    Func<T, bool> action)
{
    foreach (T value in enumerable)
        action(value);

    return validator;
}

これは、列挙型ではうまく機能しますが、継承された型/インターフェイスでは失敗します。まあ言ってみれば:

IValidator<IEnumerable<Guid>> validator = ...;

IEnumerable<Guid> guids = ...;
validator.Each(guids, guid => guid != Guid.Empty);   // ok

IList<Guid> guids = ...;
validator.Each(guids, guid => guid != Guid.Empty);   // doesn't compile (see below)

例外は次のとおりです。

IValidator<IList<Guid>>'Each' の定義が含まれておらず、タイプの最初の引数を受け入れる拡張メソッド 'Each' IValidator<IList<Guid>>が見つかりませんでした (using ディレクティブまたはアセンブリ参照がありませんか?

私の質問はIValidator<T>、より具体的には、そのジェネリック型引数の継承チェーンについてTです。IValidator<IEnumerable<T>>から型を代入できないのはなぜIValidator<IList<T>>ですか? IList<T>on ではないIEnumerable<T>(同じ が与えられた場合)と私が考えることができる状況はありませんT

ジェネリック引数を に制限することT : IEnumerable<R>はできますが、それには 2 つの型引数 (TおよびR) が必要であり、可能であれば避けたいと思います。

何かご意見は?より良い解決策はありますか? ありがとう。

4

2 に答える 2

6

これは、IValidator<T>インターフェイスの定義によるものです。私はそれが次のようなものだと確信しています:

public interface IValidator<T>

あなたが本当に欲しいのは:

public interface IValidator<out T>

これにより、インターフェースが共変になります。つまり、 がから派生IValidator<T2>するIValidator<T>と仮定して、の実装を に割り当てることができます。T2T

この場合、IList<T>は から派生してIEnumerable<T>いるため、共変として宣言できるはずTです。ただし、これはメソッドIValidator<T>とその公開方法によって異なります。

つまり、インターフェイス上の任意のメソッドへのパラメータとして inIValidator<T>のインスタンスを取るメソッドがある場合T、インターフェイスを共変として宣言することはできません。

この場合、次の の定義を回避できるはずですEach

public static IValidator<T> Each<T, TValue>(
    this IValidator<T> enumerable, 
    Func<TValue, bool> action) where T : IEnumerable<TValue>
{
    foreach (TValue value in enumerable)
        action(value);

    return validator;
}

これは、Tが から派生する必要があることを示しますIEnumerable<TValue>

于 2012-08-31T14:06:27.713 に答える
2

2つの答え:

  1. where T: IEnumerable<T>どちらが問題を解決する可能性があり、2 番目のジェネリック型を必要としないと言っても過言ではありません。

  2. IValidator<IEnumerable<T>>IValidator<IList<T>>インターフェイスがバリアントではないため、から割り当てられません。v4 の時点で、C# はインターフェイスの分散をサポートしていますが、インターフェイス定義で明示的に要求する必要があります。これを行うには、out Generic Modifier

共分散とは、代入などを行う際に、あまり派生していない型を期待するジェネリック パラメーターを、より派生した型に置き換えることを可能にするものです。

インターフェイスが本当にバリアントセーフであることを確認するのはあなた次第であることに注意してください。これが、C# 4 より前では不可能だった理由です一部のインターフェイスは安全に共変であり、一部は安全に反変です (予想される型をより少ない派生型にのみ置き換えることができます)。それはすべて、あなたが何をしているかにかかっています。

于 2012-08-31T14:07:22.207 に答える