9

ジェネリックを使用する場合のポリモーフィズムの仕組みを理解するのに問題があります。例として、次のプログラムを定義しました。

public interface IMyInterface
{
    void MyMethod();
}

public class MyClass : IMyInterface
{
    public void MyMethod()
    {
    }
}

public class MyContainer<T> where T : IMyInterface
{
    public IList<T> Contents;
}

その後、これを行うことができます。これはうまく機能します。

MyContainer<MyClass> container = new MyContainer<MyClass>();
container.Contents.Add(new MyClass());

MyInterface を実装する多くのクラスがあります。すべての MyContainer オブジェクトを受け入れることができるメソッドを書きたいと思います:

public void CallAllMethodsInContainer(MyContainer<IMyInterface> container)
{
    foreach (IMyInterface myClass in container.Contents)
    {
        myClass.MyMethod();
    }
}

では、このメソッドを呼び出したいと思います。

MyContainer<MyClass> container = new MyContainer<MyClass>();
container.Contents.Add(new MyClass());
this.CallAllMethodsInContainer(container);

それはうまくいきませんでした。確かに、MyClass は IMyInterface を実装しているので、キャストするだけでよいのでしょうか?

MyContainer<IMyInterface> newContainer = (MyContainer<IMyInterface>)container;

それもうまくいきませんでした。通常の MyClass を IMyInterface に確実にキャストできます。

MyClass newClass = new MyClass();
IMyInterface myInterface = (IMyInterface)newClass;

だから、少なくとも私はそれを完全に誤解していません。同じインターフェイスに準拠するクラスのジェネリック コレクションを受け入れるメソッドをどのように記述すればよいか、正確にはわかりません。

必要に応じて、この問題を完全に回避する計画がありますが、適切に実行したいと考えています。

前もって感謝します。

4

3 に答える 3

4

注: いずれの場合も、Contentsフィールドを実装する具体的なオブジェクトに初期化する必要があります。IList<?>

一般的な制約を維持すると、次のことができます。

public IList<T> Contents = new List<T>();

そうでない場合は、次のことができます。

public IList<MyInterface> Contents = new List<MyInterface>();

方法 1:

メソッドを次のように変更します。

public void CallAllMethodsInContainer<T>(MyContainer<T> container) where T : IMyInterface
{
    foreach (T myClass in container.Contents)
    {
        myClass.MyMethod();
    }
}

スニペットは次のようになります。

MyContainer<MyClass> container = new MyContainer<MyClass>();
container.Contents.Add(new MyClass());
this.CallAllMethodsInContainer(container);

方法 2:

CallAllMethodsInContainerまたは、次のようにメソッドをMyContainer<T>クラスに移動します。

public void CallAllMyMethodsInContents()
    {
        foreach (T myClass in Contents)
        {
            myClass.MyMethod();
        }
    }

スニペットを次のように変更します。

MyContainer<MyClass> container = new MyContainer<MyClass>();
container.Contents.Add(new MyClass());
container.CallAllMyMethodsInContents();

方法 3:

編集:さらに別の方法は、次のMyContainerようにクラスから一般的な制約を削除することです:

public class MyContainer
{
    public IList<MyInterface> Contents;
}

メソッドのシグネチャを

  public void CallAllMethodsInContainer(MyContainer container)

次に、スニペットは次のように機能するはずです。

MyContainer container = new MyContainer();
container.Contents.Add(new MyClass());
this.CallAllMethodsInContainer(container);

この代替手段では、コンテナのContentsリストは を実装するオブジェクトの任意の組み合わせを受け入れることに注意してくださいMyInterface

于 2010-08-25T12:09:23.433 に答える
3

うわー、この質問は最近たくさん出てきています。

簡単な答え:いいえ、これは不可能です。可能なことは次のとおりです。

public void CallAllMethodsInContainer<T>(MyContainer<T> container) where T : IMyInterface
{
    foreach (IMyInterface myClass in container.Contents)
    {
        myClass.MyMethod();
    }
}

そして、これがあなたが試したことが不可能な理由です(私の最近の回答から取られました):

List<T>タイプを考えてみましょう。とがList<string>ありList<object>ます。文字列はオブジェクトから派生しますが、 ;List<string>から派生したものには続きません。List<object>もしそうなら、あなたはこのようなコードを持つことができます:

var strings = new List<string>();

// If this cast were possible...
var objects = (List<object>)strings;

// ...crap! then you could add a DateTime to a List<string>!
objects.Add(new DateTime(2010, 8, 23));23));

上記のコードは、共変型である(そうでない)ことの意味を示しています。が共変である場合、(。NET 4.0では)から派生T<D>する別のタイプT<B>にタイプをキャストできることに注意してください。ジェネリック型の引数が出力の形式でのみ表示される場合、つまり、読み取り専用のプロパティと関数の戻り値の場合、ジェネリック型は共変です。DBT

このように考えてください。あるタイプT<B>が常にaを提供するB場合、すべてのsがsであるため、常にDT<D>)を提供するタイプはaとして動作できます。T<B>DB

ちなみに、ジェネリック型パラメーターが入力の形式、つまりメソッドパラメーターでのみ表示される場合、型は反変です。型が反変である場合、それは奇妙に見えるかもしれませんが、T<B>にキャストすることができます。T<D>

このように考えてください。あるタイプT<B>が常にを必要とするB場合、それは常にを必要とするタイプに介入することができます。これDもまた、すべてDのsがBsであるためです。

クラスは共変でも反変でもありませMyContainerん。これは、その型パラメーターが入力(経由Contents.Add)と出力(Contentsプロパティ自体経由)の両方のコンテキストで表示されるためです。

于 2010-08-25T12:13:30.213 に答える
1

これは共分散の問題です

http://msdn.microsoft.com/en-us/library/dd799517.aspx

MyContainer<MyClass>次のようなことができるためMyContainer<IMyInterface>、キャストできませんContents.Add(new AnotherClassThatImplementsIMyInterface())

于 2010-08-25T12:12:33.610 に答える