9

次のタイプが与えられます:

public interface IPrimary{ void doBattle(); }

// an ISecondary "is" an IPrimary
public interface ISecondary : IPrimary {  }

// An implementation of ISecondary is also an IPrimary:
internal class SecondaryImpl : ISecondary
{
    // Required, since this is an IPrimary
    public void doBattle(){ }
}

なぜ私はこれを行うことができないのですか?

List<IPrimary> list = new List<ISecondary>();

これにより、次のコンパイルエラーが発生します。

引数タイプ'System.Collections.Generic.List'はパラメータータイプ'System.Collections.Generic.List'に割り当てることができません

エラーを理解し、回避策があることを認識しています。この直接変換が許可されない明確な理由はわかりません。のリストに含まれる値は、結局のところISecondary、タイプの値であるIPrimary必要がList<IPrimary>ありList<ISecondary>ます。なぜ、無関係のタイプとして解釈されるのでしょうか。

C#がこのように設計されている理由を誰かが明確に説明できますか?

少し拡張された例:次のようなことをしようとしたときに問題が発生しました。

internal class Program
{
    private static void Main(string[] args)
    {
        // Instance of ISecondary, and by extention, IPrimary:
        var mySecondaryInstance = new SecondaryImpl();

        // This works as expected:
        AcceptImpl(mySecondaryInstance);

        // List of instances of ISecondary, which are also, 
        // by extention, instances of IPrimary:
        var myListOfSecondaries = new List<ISecondary> {mySecondaryInstance};

        // This, however, does not work (results in a compilation error):
        AcceptList(myListOfSecondaries);
    }

    // Note: IPrimary parameter:
    public static void AcceptImpl(IPrimary instance){  }

    // Note: List of type IPrimary:
    public static void AcceptList(List<IPrimary> list){  }

}
4

4 に答える 4

11
public class Animal
{
    ...
}

public class Cat: Animal
{
    public void Meow(){...}
}

List<Cat> cats = new List<Cat>();

cats.Add(new Cat());

cats[0].Meow();  // Fine.

List<Animal> animals = cats; // Pretend this compiles.

animals.Add(new Animal()); // Also adds an Animal to the cats list, since animals references cats.

cats[1].Meow(); // cats[1] is an Animal, so this explodes!

そしてそれが理由です。

于 2013-01-31T08:53:43.513 に答える
8

なぜ私はこれを行うことができないのですか?List<IPrimary> list = new List<ISecondary>();

次のように定義されたメソッドがあると想像してください。

public void PopulateList(List<IPrimary> listToPopulate)
{
    listToPopulate.Add(new Primary());  // Primary does not implement ISecondary!
}

List<ISecondary>パラメータとして渡すとどうなりますか?

List<ISecondary>から割り当てられないList<IPrimary>エラーは、そのような問題から抜け出すためのコンパイラの方法です。

于 2013-01-31T08:50:57.063 に答える
5
class Evil : IPrimary {...}
list.Add(new Evil()); // valid c#, but wouldn't work

エラーからあなたを守っています。リストインスタンス(オブジェクト)はセカンダリインスタンスを要求します。すべてのプライマリがセカンダリであるわけではありません。ただし、プライマリのリストには任意のプライマリを保持できることが期待されます。二次リストを一次リストとして扱うことができれば、悪いことです。

実際、配列これを許可します-そして、それを間違えると実行時にエラーが発生します。

于 2013-01-31T08:50:24.183 に答える
1

リストタイプがジェネリックパラメーターで共変でList<ISecondary>はない、つまりサブタイプではない理由は、リストタイプList<IPrimary>が読み取り/書き込みであるためです。拡張された例では、メソッドはであるが、ではない場合にAcceptList実行できます。list.Add(x)xIPrimaryISecondary

これは正しく共変であることに注意してくださいIEnumerable<T>。配列は共変で型付けされますが(上記で試したことを実行できます)、同じ理由でこれは適切ではありません。コレクションへの要素の追加は実行時に失敗します。

于 2013-01-31T08:51:50.843 に答える