12

大きな絵:

私はRazorの制限のように見えるものを見つけました、そして私はそれを回避する良い方法を思い付くのに苦労しています。

プレーヤー:

私がこのようなモデルを持っているとしましょう:

public abstract class BaseFooModel<T>
    where T : BaseBarType
{
    public abstract string Title { get; } // ACCESSED BY VIEW
    public abstract Table<T> BuildTable();

    protected Table<T> _Table;
    public Table<T> Table // ACCESSED BY VIEW
    {
        get
        {
            if (_Table == null)
            {
                _Table = BuildTable();
            }
            return _Table;
        }
    }
}

そして、このようなサブクラス:

public class MyFooModel : BaseFooModel<MyBarType>
{
    // ...
}

public class MyBarType : BaseBarType
{
    // ...
}

MyFooModel次のように定義されているかみそりのビューに渡せるようにしたいと思います。

// FooView.cshtml
@model BaseFooModel<BaseBarType>

しかし、それはうまくいきません。FooView期待しているBaseFooModel<BaseBarType>が取得するという実行時エラーが発生しますMyFooModelMyFooModelからの継承とからBaseFooModel<MyBarType>MyBarType継承を思い出してくださいBaseBarType

私が試したこと:

私はこれをかみそり以外の土地で試して、同じことが当てはまるかどうかを確認しました。ビューを機能させるには、ビューでテンプレートパラメータを使用する必要がありました。これがその非かみそりの見方です:

public class FooView<T>
    where T : BaseBarType
{
    BaseFooModel<T> Model;
    public FooView(BaseFooModel<T> model)
    {
        Model = model;
    }
}

その構造では、以下が機能します。

new FooView<MyBarType>(new MyFooModel());

私の質問:

どうすればRazorでそれを行うことができますか?どうすれば私がやっているようなタイプを渡すことができFooViewますか?
できませんが、これを回避する方法はありますか?どういうわけか同じアーキテクチャを実現できますか?

さらに情報を提供できるかどうか教えてください。.NET4とMVC3を使用しています。


編集:
今のところ、のサブクラスごとにかみそりのビューを追加していますBaseFooModel<BaseBarType>。新しいモデルを追加するたびに新しいビューを作成する必要がないので、私はそれについては気になりません。

もう1つのオプションは、これを通常のc#クラスでかみそりなしで機能させることができるという事実を利用することです。かみそり@inheritsにc#ビューを表示させてから、renderメソッドを呼び出すことができます。HTMLをレンダリングする2つの方法が好きではないので、このオプションは嫌いです。

他のアイデアはありますか?Fooとでクラス名を付けると、問題のコンテキストを理解するのが難しいことはわかっていますBarが、少し機密性が高いため、あまり多くの情報を提供することはできません。それについてお詫び申し上げます。


ベンジャミンの答えを使って、私がこれまでに持っているもの:

public interface IFooModel<out T> 
    where T : BaseBarModel
{
    string Title { get; }
    Table<T> Table { get; } // this causes an error:
                            // Invalid variance: The type parameter 'T' must be 
                            // invariantly valid on IFooModel<T>.Table. 
                            // 'T' is covariant.
}

public abstract class BaseFooModel<T> : IFooModel<T>
    where T : BaseBarModel
{
    // ...
}

何がうまくいったのか:

public interface IFooModel<out T> 
    where T : BaseBarModel
{
    string Title { get; }
    BaseModule Table { get; } // Table<T> inherits from BaseModule
                              // And I only need methods from BaseModule
                              // in my view. 
}

public abstract class BaseFooModel<T> : IFooModel<T>
    where T : BaseBarModel
{
    // ...
}
4

3 に答える 3

21

共変のジェネリック型パラメーターを持つインターフェースをクラス階層に導入する必要があります。

public interface IFooModel<out T> where T : BaseBarType
{
}

そして、上記のインターフェースからBaseFooModelを派生させます。

public abstract class BaseFooModel<T> : IFooModel<T> where T : BaseBarType
{
}

コントローラ内:

[HttpGet]
public ActionResult Index()
{
    return View(new MyFooModel());
}

最後に、ビューのモデルパラメータを次のように更新します。

@model IFooModel<BaseBarType>
于 2013-03-28T20:44:50.183 に答える
2

インターフェイスベースのモデルの使用は、ASP.NETMVC2とMVC3の間で意図的に変更されました。

あなたはここで見ることができます

MVCチーム:

インターフェイスベースのモデルを持つことは、私たちが推奨するものではありません(また、バグ修正によって課せられた制限を考えると、現実的にサポートすることはできません)。抽象基本クラスに切り替えると、問題が修正されます。

「スコット・ハンゼルマン」

于 2013-03-31T08:40:25.910 に答える
0

発生している問題は、Razorエラーではなく、C#エラーです。クラスでそれを実行しようとすると、同じエラーが発生します。これは、モデルがではなくBaseFooModel<BaseBarType>BaseFooModel<MyFooModel>であり、2つの間で暗黙的な変換を行うことができないためです。通常、プログラムでは、それを行うために変換を行う必要があります。

ただし、.NET 4では、反変性と共分散が導入されました。これは、探しているものの能力のように聞こえます。これは.NET4の機能のみであり、.NET4のRazorがそれを利用しているかどうかは正直わかりません。

于 2013-03-26T20:56:02.437 に答える