私にとって、そのようにするのは完璧です:
public class A { }
public class B : A { }
public class C
{
public List<A> b = new List<B>();
}
List は要素がクラス A であることを期待しますが、これは List にも当てはまります。型の不一致があることは知っていますが、論理的には、コンパイラがそのような不一致を許可することは完全に理にかなっています。なぜだめですか?
私にとって、そのようにするのは完璧です:
public class A { }
public class B : A { }
public class C
{
public List<A> b = new List<B>();
}
List は要素がクラス A であることを期待しますが、これは List にも当てはまります。型の不一致があることは知っていますが、論理的には、コンパイラがそのような不一致を許可することは完全に理にかなっています。なぜだめですか?
List<T>
では共変であることが期待されていますT
が、そうではありません。
C# では、一部のインターフェイスは共変ですが、クラスは共変ではありません。上記のように、言うのはA
合法ですB
IEnumerable<A> b = new List<B>();
また
IReadOnlyList<A> b = new List<B>();
問題のインターフェースは共変であるため、つまり、次のように「out」で宣言されます
public interface IEnumerable<out T> ...
public interface IReadOnlyLies<out T> ...
クラスを使用してこれを達成することはできません。インターフェイスを使用することはできますが (共分散と反分散を調べます)、List
/を使用する場合はできませんIList
。次のシナリオを想像してください。
public class MyClass<T>
{
T GetT() { /* Blah blah */ }
void SetT(T value) { /* Blah Blah */ }
}
これを書く:
MyClass<object> example = new MyClass<string>();
それは最初の方法で機能します。example
を返す必要があり、ポリモーフィズムのために正当な文字列object
を返します。MyClass<string>
2 番目の方法は、より問題があります。後でこれを書いたとしましょう:
example.SetT(new object());
これは不正MyClass<string>
ですstring
。object
良くない。
前述のように、共分散と反分散を使用して、インターフェイスに対してこれを機能させることができます。次のようなインターフェイスを作成できます。
public interface Covariant<out T>
{
T FunctionReturningT();
}
public class MyInterfaceImplementation<T> : Covariant<T>
{
public T FunctionReturningT() { /* Blah Blah */ }
}
インターフェイスをcovariantにします。つまり、次のように記述しても問題ありません。
Covariant<object> example = new MyInterfaceImplementation<string>();
また、次のように書くこともできます。
public interface Contravariant<in T>
{
void FunctionAskingForT(T value);
}
public class MyInterfaceImplementation<T> : Contravariant<T>
{
public void FunctionAskingForT(T value) { /* Blah Blah */ }
}
そのインターフェイスをcontravariantにしました。つまり、次のように記述しても問題ありません。
Contravariant<string> example = new MyInterfaceImplementation<object>();
リストがList<A>
変数に格納されている場合、プログラムはその変数に型のオブジェクトを配置できると想定しているためA
です。List<B>
オブジェクトを変数に格納すると、 typeを保持できると明示的に宣言されているリストList<A>
に type のオブジェクトを入れることができなくなります。 A
A
あれは:
List<A> b = new List<B>()
// compiler knows list should be of type A, so it expects this to work:
b.add(new A());
List<B>
ただし、変数に aを割り当てることができた場合、変数が型であることをList<A>
コンパイラが認識しているため、型オブジェクトを保持できるはずであっても、型エラーが発生します。b
List<A>
A
代わりに、許可されているnew List<A>
型の要素を使用して追加するB
か、変数の型を に変更しますList<B>
。