大图:
我发现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>
,但获得MyFooModel
。回想一下,来自MyFooModel
和BaseFooModel<MyBarType>
的herits中的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
的类型?
我不能,但有什么方法吗?我可以以某种方式实现相同的架构吗?
如果我能提供更多信息,请告诉我。我正在使用.NET 4和MVC 3.
修改
现在,我只是为BaseFooModel<BaseBarType>
的每个子类添加一个剃刀视图。我对此并不感到兴奋,因为每次添加新模型时我都不想创建新视图。
另一种选择是利用我 能够在没有剃刀的情况下在常规c#类中工作的事实。我可以让我的剃刀视图@inherits
c#视图,然后调用一些渲染方法。我不喜欢这个选项,因为我不喜欢有两种渲染html的方法。
还有其他想法吗?当我用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
{
// ...
}
答案 0 :(得分:19)
您需要在类层次结构中引入一个带有covariant泛型类型参数的接口:
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>
答案 1 :(得分:1)
在ASP.NET MVC 2和MVC 3之间故意改变使用基于接口的模型。
MVC团队:
基于接口的模型不是我们鼓励的(也没有,鉴于错误修复所施加的限制,可以实际支持)。切换到抽象基类可以解决问题。
“Scott Hanselman”
答案 2 :(得分:0)
您遇到的问题不是Razor错误,而是C#错误。尝试用类来做,你会得到同样的错误。这是因为模型不是BaseFooModel<BaseBarType>
,而是BaseFooModel<MyFooModel>
,并且两者之间不会发生隐式转换。通常,在程序中,您必须进行转换才能执行此操作。
然而,在.NET 4中,引入了contravariance and covariance,这听起来像是你正在寻找的能力。这只是一个.NET 4功能,老实说我不知道.NET 4中的Razor是否使用它。