4

相互にリンクされたいくつかの汎用インターフェイスがあります。

public interface IA
{
    int val { get; set; }
}
public interface IB<T> where T:IA
{
    T a_val { get; set; }
}
public interface IC<T> where T : IB<IA>
{
    T b_val { get; set; }
}

public class a:IA
{
    public int val { get; set; }
}
public class b:IB<a>
{
    public a a_val { get; set; }
}
public class c:IC<b>
{
    public b b_val { get; set; }
}

最後のクラス c については、エラーがあります。

型 'b' は、ジェネリック型またはメソッド 'IC' の型パラメーター 'T' として使用できません。'b' から 'IB' への暗黙的な参照変換はありません。

この場合、ジェネリック インターフェイスを適切に使用するにはどうすればよいですか?

4

2 に答える 2

7

TIC<T>ある必要がありIB<IA>ます。あなたはそれを与えましたIB<A>。が実装されているという理由だけで、IB<A>がとして使用できるという保証はありません。IB<IA>AIA

このように考えてみてください: ifIB<T>は「私はタイプ T のものなら何でも食べられる」をIA意味し、「私は果物です」をA意味し、「りんご」をIB<IA>意味しIB<A>ます。りんご"。一部のコードがバナナとブドウを食べさせたい場合は、IB<IA>ではなく を取る必要がありIB<A>ます。

IB<A>に変換できると仮定してIB<IA>、何が問題になるかを見てみましょう。

class AppleEater : IB<Apple> 
{  
    public Apple a_val { get; set; }
}
class Apple : IA 
{
    public int val { get; set; }
}
class Orange : IA 
{
    public int val { get; set; }
}
...
IB<Apple> iba = new AppleEater();
IB<IA> ibia = iba; // Suppose this were legal. 
ibia.a_val = new Orange(); // ibia.a_val is of type IA and Orange implements IA

これは、 typeのオブジェクトへの参照へiba.valの type のプロパティです。AppleOrange

そのため、変換は違法でなければなりません。

では、どうすればこれを合法化できるでしょうか。

先ほど示したように、タイプセーフではないため、コードがそのままの状態ではできません。

T次のoutようにマークすることで、合法的にすることができますinterface IB<out T>。ただし、T「入力コンテキスト」で使用することは違法です。T特に、セッターを持つ型のプロパティを持つことはできません。その制限を行うと、読み取り専用であるためa_valのインスタンスに設定できないため、問題はなくなります。Orange

この質問は、SO で非常に頻繁に尋ねられます。多くの例については、C# の「共分散と反分散」に関する質問を探してください。

于 2013-03-04T19:32:58.960 に答える
3

もっと簡単に(そしてもう少しきれいに)できるかどうかはわかりませんが、このコードはコンパイルされます:

public interface IA
{
    int val { get; set; }
}
public interface IB<T> where T : IA
{
    T a_val { get; set; }
}
public interface IC<T, U> where T : IB<U> where U : IA
{
    T b_val { get; set; }
}

public class a : IA
{
    public int val { get; set; }
}
public class b : IB<a>
{
    public a a_val { get; set; }
}
public class c : IC<b, a>
{
    public b b_val { get; set; }
}

さらに重要なことは、次のようなことをさせないことです。

public class a1 : IA
{
    public int val { get; set; }
}

public class c : IC<b, a1>
{
    public b b_val { get; set; }
}

コンパイラは次のエラーをスローします。

型 'ConsoleApplication2.b' は、ジェネリック型またはメソッド 'ConsoleApplication2.IC' の型パラメーター 'T' として使用できません。「ConsoleApplication2.b」から「ConsoleApplication2.IB」への暗黙的な参照変換はありません。

そして、それは本当にクールな機能ですね。

于 2013-03-04T19:30:30.457 に答える