我正在深入研究C#类的自定义强制转换。这个StackOverflow question给了我一个良好的开端,但是我的类中有类型参数。一个例子如下:
说我的实体有这个类层次结构:
public class Animal
{
}
public class Tiger : Animal
{
public int NumberOfStripes { get; set; }
}
(其中Tiger有一些Animal没有的属性)。
现在,我尝试执行自定义强制转换的类与下面的类似(我使用ASP.NET MVC作为旁注)
public class SomeViewModel<T> where T : Animal
{
public T Animal { get; set; }
...
}
在为此类层次结构创建编辑表单时,我需要特定于Animal
子类的表单,但实际animal
的实例化是使用Type
对象完成的。在某些时候,我需要将SomeViewModel<Animal>
转换为SomeViewModel<Tiger>
,以便我可以使用强类型的Razor视图。
示例控制器:
namespace MvcProject.Controllers
{
public class AnimalsController : Controller
{
public ActionResult Create(int id)
{
AnimalType t = DbContext.AnimalTypes.Find(id); // get AnimalType object from database
AnimalViewModel<Animal> model = new AnimalViewModel<Animal>()
{
Animal = (Animal)t.CreateInstance() // Returns a Tiger object cast to an Animal
};
return View(model);
}
}
}
在我的Razor视图中,我想渲染一个强类型的部分到Tiger
类,如下所示:
@Html.Partial("_TigerForm", Model)
_TigerForm Razor部分内容:
@model AnimalViewModel<Tiger>
<p>Number of Stripes: @Model.Animal.NumberOfStripes</p>
这里有几个问题:
AnimalViewModel<Animal>
到AnimalViewModel<Tiger>
)吗?编辑#1
请考虑以下代码:
List<Animal> a = new List<Animal>();
List<Tiger> b = (List<Tiger>)a;
Visual Studio仍然抱怨它无法转换对象。这似乎是我问题的根源。
编辑#2
此代码确实有效:
Animal a = new Tiger();
Tiger b = (Tiger)a;
答案 0 :(得分:1)
如果您使用了接口,则可以实现此目的。
所以你的模型类将成为:
public interface IAnimal
{
}
public Interface ITiger : IAnimal
{
int NumberOfStripes { get; set; }
}
public class Animal : IAnimal
{
}
public class Tiger : Animal, ITiger
{
public int NumberOfStripes { get; set; }
}
现在您的视图模型将更改为接受界面:
public class SomeViewModel<T> where T : IAnimal
{
public T Animal { get; set; }
...
}
然后你的Razor视图可以使用接口,并且转换将起作用,因为模型实现了所需的接口:
@model AnimalViewModel<IAnimal>
@Html.Partial("_TigerForm", Model)
_TigerForm Razor部分内容:
@model AnimalViewModel<ITiger>
<p>Number of Stripes: @Model.Animal.NumberOfStripes</p>
希望有所帮助!
答案 1 :(得分:1)
您的问题是您没有创建正确的视图模型。
public ActionResult Create(int id)
{
AnimalType t = DbContext.AnimalTypes.Find(id); // get AnimalType object from database
AnimalViewModel<Animal> model = new AnimalViewModel<Animal>()
{
Animal = (Animal)t.CreateInstance() // Returns a Tiger object cast to an Animal
};
return View(model);
}
您需要做的是获取正确的ViewModel类型的句柄,并使用正确的泛型创建该实例。动物将永远是动物,无论你是将它设置为老虎还是其他任何东西,除非你将它投射到它应该是什么或试图动态调用一切。
下面的代码实际上会为您创建AnimalViewModel<Tiger>
而不是AnimalViewModel<Animal>
,这会导致您的问题。
public class AnimalViewModel<T> where T : Animal
{
public T Animal { get; set; }
public AnimalViewModel(T animal)
{
Animal = animal;
}
}
public ActionResult Create(int id)
{
AnimalType t = DbContext.AnimalTypes.Find(id); // get AnimalType object from database
Animal animal = (Animal)t.CreateInstance();
var animalViewModel =
Activator.CreateInstance(typeof(AnimalViewModel<>).MakeGenericType(animal.GetType()),
animal)
return View(animalViewModel);
}
答案 2 :(得分:0)
如果你想使用相同的&#34; base&#34;查看,然后根据Animal
的类型放置不同的部分,现在唯一的解决方案是if
:
@if (Model is Tiger)
{
@Html.Partial("_TigerForm", Model)
}
你不需要强制转换Model,因为View中的这种绑定是在运行时完成的,所以它不会给你编译错误(可能是警告,是的)。
当然,在您开始添加大量此if
时,您应该开始考虑创建一个Html Helper,它将根据类型呈现正确的Partial。
更好的是,您可以使用某种惯例并执行此操作:
@Html.Partial("_" + Model.GetType().Name + "Form", Model)
免责声明:未经测试,可能会比看起来更有问题。 :)