如何在C#中使用Type参数在两个类之间提供自定义强制转换

时间:2014-03-21 17:22:39

标签: c# asp.net-mvc-4 razor casting

我正在深入研究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>

这里有几个问题:

  1. 你可以做这种类型的演员表(从AnimalViewModel<Animal>AnimalViewModel<Tiger>)吗?
  2. 还有哪些其他选项可以不需要进行类型转换?如果可能的话,我想避免使用动态视图。
  3. 编辑#1

    请考虑以下代码:

    List<Animal> a = new List<Animal>();
    List<Tiger> b = (List<Tiger>)a;
    

    Visual Studio仍然抱怨它无法转换对象。这似乎是我问题的根源。

    编辑#2

    此代码确实有效:

    Animal a = new Tiger();
    Tiger b = (Tiger)a;
    

3 个答案:

答案 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)

免责声明:未经测试,可能会比看起来更有问题。 :)