1

いくつかの依存関係を実装しました (MVP パターンの一部です)。今、キャストを実行しようとすると、VS がエラーを通知します。

定義:

interface IView
{
     void setPresenter(IPresenter<IView> presenter);
}

interface IViewA : IView
{
}

interface IPresenter<T> where T : IView
{
    void setView(T view);
}

class PresenterA : IPresenter<IViewA>
{
}

暗黙のキャスト:

IPresenter<IView> presenter = new PresenterA();

コンパイル エラー: 型 'PresenterA' を 'IPresenter' に暗黙的に変換できません。明示的な変換が存在します (キャストがありませんか?)

明示的なキャスト:

IPresenter<IView> presenter = (IPresenter<IView>)new PresenterA();

実行時エラー: InvalidCastException

この概念を維持するにはどうすれば解決できますか?ジェネリック型の概念 (私の前のものはそれがありません)。他の投稿に記載されているように、分散と反分散の問題を(インとアウトで)試しましたが、エラーもありました(VS 2010の下)。

4

4 に答える 4

4

から派生したという事実は、 がからIViewA派生したことをIView自動的に意味するわけではありません。実際、 とは、その間に継承関係がない 2 つの異なる型です。彼らの唯一の共通の祖先は.IPresenter<IViewA>IPresenter<IView>IPresenter<IViewA>IPresenter<IView>object

例を見てみましょう。クラスAnimalCatから派生しAnimalたクラス 、およびからDog派生したクラス があるとしAnimalます。では、2 つのリストを宣言しましょう

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

また、次の割り当てが可能であると仮定しましょう。

animals = cats;
animals.Add(new Cat()); // OK
animals.Add(new Dog()); // Ooops!

リストは実際には猫のリストであり、犬を追加しようとしています! したがって、2 つのタイプList<Animal>List<Cat>は代入の互換性を持つことができません。

于 2013-02-08T19:00:06.750 に答える
2

この質問は、ジェネリック型の共変性と反変性に関するものです。我々は持っています

アンIViewAはアンですIView

しかし、それは自動的にそれを意味するわけではありません

アンIPresenter<IViewA>はアンですIPresenter<IView>

結論が成り立つときIPresenter<T>、は で共変であると言いTます。C# では、次のように、問題の型パラメーター (ここでは)outの前にキーワードを配置することにより、インターフェイス共変を作成します。T

interface IPresenter<out T>  ...

outキーワードを入れていないので、あなたのすることは許されません。

しかし、型を共変にするのが安全なTのは、すべての使用Tが「アウト」になる場合だけです。たとえば、メソッドの戻り型として使用したり、 -only プロパティTのプロパティ型として使用したりしてもかまいません。get

インターフェイスはT、「in」位置で、つまり値パラメーターとして使用します (つまり、ref(またはout) 修飾子のないパラメーター)。したがって、インターフェイスを共変にすることは違法です。この制限がない場合に何が起こるかの例については、他の回答のいくつかを参照してください。

次に、反変性の概念があります。これは、 forIPresenter<>

アンIViewAはアンですIView

ことを意味します

アンIPresenter<IView>はアンですIPresenter<IViewA>

反変性によって順序がどのように変化するかに注目してください。反変性は、型パラメーターが値パラメーターなどの "in" 位置で使用されている場合にのみ安全です (そして許可されます)。

唯一のメンバーに基づいて、インターフェイスの反変を宣言することは合法です。

interface IPresenter<in T>  ...

もちろん、 whereinは反変を意味します。ただし、暗黙的な変換が許可される「方向」が逆になります。

于 2013-02-08T19:51:01.420 に答える
1

に a を格納することPresenterAで、IPresenter<IView>「このオブジェクトにsetViewは任意のメソッドを受け入れるIView」ということになります。

ただし、PresenterAのメソッドsetViewは のみを受け入れますIViewA。を渡した場合IViewSomethingElse、何が起こると思いますか?

動作しないため、コンパイラは許可しません。

于 2013-02-08T19:02:09.600 に答える
0

あなたがやろうとしていることは意味がありません。次のことを想像してください (意味のある 1 つの差異を含めて):

interface IView {}

interface IViewA : IView {}
class ViewA : IViewA {}

interface IViewB : IView {}
class ViewB : IViewB {}

interface IPresenter<in T> where T : IView
{
    void setView(T view);
}

class PresenterA : IPresenter<IViewA>
{
    public void setView(IViewA view) {}
}

class PresenterB : IPresenter<IViewB>
{
    public void setView(IViewA view) {}
}

実行しようとしている変換が有効である場合は、次のようにすることができます。

IPresenter<IView> presenter = new PresenterA();
presenter.setView(new ViewB());

ご覧のとおり、これはタイプセーフではありません。つまり、タイプ間に存在すると思われる関係は存在しません。

バリアンスでできることは、その逆です。

class Presenter : IPresenter<IView>
{
    public void setView(IView view) {}
}

IPresenter<IViewA> presenter = new Presenter();

Presenter.setView()任意のIViewパラメーターを受け入れることができるため、IViewB. これは、コンパイラが明示的な変換について言及している理由でもあります。次のことができるようにするためです。

IPresenter<IViewA> presenterA = new Presenter();
IPresenter<IView> presenter = (IPresenter<IView>) presenterA;

つまり、presenter実行時に割り当てている値がたまたま「十分に一般的」なものであるかどうかを確認するためです。コンパイル時の型がそうでなくてもです。

于 2013-02-08T19:09:10.913 に答える