16

これは少し長い質問ですので、ご容赦ください。

文字列のセットと、各文字列に対応するジェネリックメソッド呼び出しとの間にマッピングを作成する必要があります。しかし、私はコンパイルの問題に遭遇しました、下の方で説明しました。

私のシナリオでは、を使用していますDictionary<>が、問題は。にも同様に存在しList<>ます。簡単にするためList<>に、以下の例ではaを使用しています。

次の3つのクラスを検討してください。

public abstract class MyBase { /* body omitted */  }
public class MyDerived1 : MyBase { /* body omitted */  }
public class MyDerived2 : MyBase { /* body omitted */  }

そして、他のクラスのメソッド:

public class Test
{
    public T GetT<T>() where T : MyBase { /* body omitted */ }
}

List<Func<MyBase>>別のクラスでは、次のように宣言できます。

public class SomeClass
{
    public void SomeFunc()
    {
        var test = new Test();

        var list1 = new List<Func<MyBase>>
            {
                test.GetT<MyDerived1>,
                test.GetT<MyDerived2>
            };
    }
}

これはすべて問題ありません。

しかし、次のようなジェネリッククラスを返す関数が必要な場合はどうなりますか?

public class RetVal<T> where T : MyBase { /* body omitted */ }

public class Test
{
    public RetVal<T> GetRetValT<T>() where T : MyBase
    {
        return null;
    }
}

List<>そして、この関数を使用して同等のものを作成したいと思います。つまり、リスト>>?

public class Class1
{
    public void SomeFunc()
    {
        var test = new Test();

        var list2 = new List<Func<RetVal<MyBase>>>
            {
                test.GetRetValT<MyDerived1>, // compile error
                test.GetRetValT<MyDerived2> // compile error
            };
    }
}

のコンパイルエラーが発生しExpected a method with 'RetVal<MyBase> GetRetValT()' signatureます。

それで、これを回避する方法はありますか、または文字列を作成するために使用できる代替アプローチがあります...ジェネリックメソッド呼び出しマッピング?

4

4 に答える 4

16

C#では、インターフェイスでの共分散のみが許可されます。つまりRetVal<MyDerived1>、をRetVal<MyBase>自動的にキャストすることはできません。共変である必要がある場合RetValは、次のようにそのインターフェイスを作成します。

public interface IRetVal<out T>
{

}
public class RetVal<T> : IRetVal<T> where T : MyBase { /* body omitted */ }

public class Test
{
    public IRetVal<T> GetRetValT<T>() where T : MyBase
    {
        return null;
    }
}

その後、このコードは機能します:

    var list2 = new List<Func<IRetVal<MyBase>>>
        {
            test.GetRetValT<MyDerived1>,
            test.GetRetValT<MyDerived2>
        };
于 2012-05-11T15:48:37.123 に答える
7

問題は、ジェネリックスの古典的な共変性/反変性です。あなたは、から継承するのでMyDerived1、からMyDerived2継承すると仮定していますが、そうではありません。MyBaseRetVal<MyDerived1>RetVal<MyBase>

これを修正する最も簡単な方法は、おそらくコードを次のように変更することです。

var list2 = new List<Func<RetVal<MyBase>>>
        {
            () => (MyBase)test.GetRetValT<MyDerived1>,
            () => (MyBase)test.GetRetValT<MyDerived2>
        };

またはさらに良いことに、JSがコメントで指摘しているように、RetVal<T>可能であれば共変になるように変更してください。

public interface IRetVal<out T> { ... }

public class RetVal<T> : IRetVal<T> { ... }
于 2012-05-11T15:46:33.657 に答える
3

クラスのジェネリック型パラメーターは共変にすることはできませんが、インターフェースの場合は共変にすることができます。typeパラメーターが共変として宣言されている場合は、クラスのIRetVal<T>代わりにインターフェースを使用して、必要なことを実行できます。 インターフェイスタイプパラメータを共変として宣言するには、「出力」位置でのみ使用する必要があります。RetVal<T>

説明のために、このコードはコンパイルされません。

interface IRetVal<out T>
{
    T Value { get; }
    void AcceptValue(T value);
}

コードをコンパイルするにはout、typeパラメーターから修飾子を削除するか、AcceptValueメソッドを削除する必要があります(パラメーターにTを使用するため:入力位置)。

インターフェイスを使用すると、次のIRetVal<out T>ことができます。

public class MyBase { }
public class MyDerived1 : MyBase { }
public class MyDerived2 : MyBase { }

public interface IRetVal<out T> where T : MyBase { /* body omitted */ }

public class Test
{
    public IRetVal<T> GetRetValT<T>() where T : MyBase
    {
        return null;
    }
}

public class Class1
{
    public void SomeFunc()
    {
        var test = new Test();

         var list = new List<Func<IRetVal<MyBase>>> 
        { 
            test.GetRetValT<MyDerived1>,
            test.GetRetValT<MyDerived2>
        };
    }
}
于 2012-05-11T16:01:31.463 に答える
2

最近、同様の問題が発生しました。

C#は、インターフェイスの実装または仮想メソッドのオーバーライドを目的とした戻り型の共分散をサポートしていません。詳細については、次の質問を参照してください。

C#はリターンタイプの共分散をサポートしていますか?

あなたはこれをハックすることができるかもしれません、私のやっています:

public RetVal<R> GetRetValT<T,R>() where T : MyBase where R : MyBase
{
    return null;
}

//Then, change this to:
test.GetRetValT<MyDerived1, MyBase>
于 2012-05-11T15:46:26.403 に答える