20

思いついたインターフェイスのキャストに頭を悩ませています。これは、C#Windowsフォーム用のMVPデザインです。フォームクラスに実装するIViewクラスがあります。私がさまざまな特定のプレゼンターに導き出したIPresenterもあります。各プレゼンターは、役割に応じてIViewを異なる方法で管理します。たとえば、フォームにデータをプリロードするEditPresenterで既存のデータを編集するのではなく、ダイアログを開いてAddPresenterで新しいデータセットを入力します。これらはそれぞれIPresenterから継承します。コードをそのまま使用したい:

AddPresenter<ConcreteView> pres = new AddPresenter<ConcreteView>();

私は基本的にこれを機能させていますが、これらのプレゼンターとそれらが管理するビューは、実行後にロードされるプラグインにバンドルされています。つまり、プラグインインターフェイスとして機能するManagerクラスが「mode」パラメーターを取る必要があります。このモードパラメータは、プレゼンターの追加または編集を作成するファクトリメソッドに使用されますが、ダイアログを表示するための呼び出しは後で行われるため、次のようにIPresenterインターフェイスを介して呼び出す必要があります。

private IPresenter<IView> pres;
public ShowTheForm()
{
    pres.ShowDialog();
}

AddPresenterの具体的なインスタンス化を「pres」メンバーにケーシングすることになると、問題が発生します。これが私が持っているものの簡略化されたバージョンです:

interface IView
{
    void ViewBlah();
}

interface IPresenter<V> where V : IView
{
    void PresBlah();
}

class CView : IView
{
    public void ViewBlah()
    {        
    }
}

class CPresenter<T> : IPresenter<T> where T : IView
{
    public void PresBlah()
    {
    }
}

private void button3_Click(object sender, EventArgs e)
{
    CPresenter<CView> cpres = new CPresenter<CView>();
    IPresenter<IView> ipres = (IPresenter<IView>)cpres;
}

これはエラーです:

Unable to cast object of type 'CPresenter`1[MvpApp1.MainForm+CView]' to type 'IPresenter`1[MvpApp1.MainForm+IView]'.

私が言えることから、PresenterとGenericの両方の型の仕様は、インターフェイスのサブクラスであるため、キャストされない理由がわかりません。

何かご意見は?

スティーブ

4

2 に答える 2

32

問題は、ジェネリック型パラメーターです。インターフェイス パラメーターを共変にすると、キャストが機能します。

これはout、次のようにキーワードを追加することで実現されます。

interface IPresenter<out V> where V : IView
{
    void PresBlah();

}

これがどのように機能するかについては、次の MSDN 記事を参照してください: Covariance and Contravariance in Generics共変型パラメーターを使用したジェネリック インターフェイスのセクションは、特にあなたの質問に適用されます。

更新: @phoog と私の間のコメントを必ず確認してください。実際のコードが aVを入力として受け入れる場合、それを共変にすることはできません。参照記事と @phoog の回答では、このケースについてさらに詳しく説明しています。

于 2012-09-11T08:53:29.533 に答える
10

CPresenter<CView>ではないのIPresenter<IView>と同様に、 も でList<int[]>はありませんIList<IEnumerable>

考えてみてください。IList<IEnumerable>a への参照を取得できた場合List<int>、それに astring[]を追加できますが、例外をスローする必要があります。静的型チェックの要点は、そのようなコードのコンパイルを防ぐことです。

インターフェイスで許可されている場合は、型パラメーターを covariant ( として宣言できます。そうすれば、インターフェイスIPresenter<out V> where V : ...は のように動作しますIEnumerable<out T>。これは、型パラメーターが入力位置で使用されない場合にのみ可能です。

List<int[]>例に戻ると、参照には何も追加できないため、これを として扱っても安全です。あなたはそれから物事を読むことしかできず、順番に をとして扱っても安全なので、すべて問題ありません。IEnumerable<IEnumerable>IEnumerable<T>int[]IEnumerable

于 2012-09-11T08:54:19.120 に答える