简体   繁体   English

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

[英]How do I provide custom cast between two classes with Type arguments in C#

I'm digging in to custom casts for C# classes. 我正在深入研究C#类的自定义转换。 This StackOverflow question gave me a head start, but my class has Type arguments in it. 这个StackOverflow问题给了我一个良好的开端,但我的类中有类型参数。 An example is below: 一个例子如下:

Say I have this class hierarchy for my Entities: 假设我的实体有这个类层次结构:

public class Animal
{
}

public class Tiger : Animal
{
    public int NumberOfStripes { get; set; }
}

(where Tiger has some properties that Animal does not). (Tiger有一些Animal没有的属性)。

Now, the class I'm trying to perform a custom cast on is similar to the one below (I'm using ASP.NET MVC as a side note) 现在,我正在尝试执行自定义强制转换的类与下面的类似(我使用ASP.NET MVC作为旁注)

public class SomeViewModel<T> where T : Animal
{
    public T Animal { get; set; }
    ...
}

When creating the edit forms for this class hierarchy, I need forms specific to an Animal sub class, but the instantiation of the actual animal is done using a Type object. 在为此类层次结构创建编辑表单时,我需要特定于Animal子类的表单,但实际animal的实例化是使用Type对象完成的。 At some point, I need to cast SomeViewModel<Animal> to SomeViewModel<Tiger> so I can use a strongly typed Razor view. 在某些时候,我需要将SomeViewModel<Animal>SomeViewModel<Tiger>以便我可以使用强类型Razor视图。

An example controller: 一个示例控制器:

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);
        }
    }
}

In my Razor view, I want to render a partial that is strongly typed to the Tiger class like so: 在我的Razor视图中,我想渲染一个强类型为Tiger类的部分,如下所示:

@Html.Partial("_TigerForm", Model)

And the contents of the _TigerForm Razor partial: 并且_TigerForm Razor的内容部分:

@model AnimalViewModel<Tiger>

<p>Number of Stripes: @Model.Animal.NumberOfStripes</p>

So couple of questions here: 这里有几个问题:

  1. Can you do this sort of type cast (from AnimalViewModel<Animal> to AnimalViewModel<Tiger> )? 你能做这种类型的转换(从AnimalViewModel<Animal>AnimalViewModel<Tiger> )吗?
  2. What other options would be available that would not require type casting? 还有哪些其他选项可以不需要进行类型转换? I'd like to avoid a Dynamic view if possible. 如果可能的话,我想避免使用动态视图。

Edit #1 编辑#1

Consider the following code: 请考虑以下代码:

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

Visual Studio still complains that it cannot cast the object. Visual Studio仍然抱怨它无法转换对象。 This seems to be the root of my problem. 这似乎是我问题的根源。

Edit #2 编辑#2

This code does work though: 这段代码确实有效:

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

If you used interfaces you can achieve this. 如果您使用了接口,则可以实现此目的。

So your model classes would become: 所以你的模型类将成为:

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; }
}

Now your view model would change to accept the interface: 现在您的视图模型将更改为接受界面:

public class SomeViewModel<T> where T : IAnimal
{
    public T Animal { get; set; }
    ...
}

Then your Razor view can use the interfaces and the casting will work because the model implements the required interfaces: 然后你的Razor视图可以使用接口,并且转换将起作用,因为模型实现了所需的接口:

@model AnimalViewModel<IAnimal>

@Html.Partial("_TigerForm", Model)

And the contents of the _TigerForm Razor partial: 并且_TigerForm Razor的内容部分:

@model AnimalViewModel<ITiger>

<p>Number of Stripes: @Model.Animal.NumberOfStripes</p>

Hope that helps! 希望有所帮助!

Your problem is that you are not creating the correct view model. 您的问题是您没有创建正确的视图模型。

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);
}

What you need to do is get a handle on the correct ViewModel type and create an instance of that with the correct generic. 您需要做的是获取正确的ViewModel类型的句柄,并使用正确的泛型创建该实例。 An Animal will always be an Animal no matter if you set it to a tiger or anything else unless you cast it to what it is supposed to be or attempt to call everything dynamically. 动物将永远是动物,无论你是将它设置为老虎还是其他任何东西,除非你把它投射到它应该是什么或试图动态调用一切。

The code below will actually create you an AnimalViewModel<Tiger> instead of AnimalViewModel<Animal> which is causing your issues. 下面的代码实际上会创建一个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);
}

If you want to use the same "base" view and then put different partials based on the type of Animal the only solution that pops into mind now is an if : 如果你想使用相同的“基础”视图然后根据Animal的类型放置不同的部分,现在想到的唯一解决方案是if

@if (Model is Tiger)
{
    @Html.Partial("_TigerForm", Model)
}

You should not need to cast Model, since this kind of binding in the View is done at runtime so it will not give you compile errors (maybe warnings, yes). 你不应该需要强制转换Model,因为View中的这种绑定是在运行时完成的,所以它不会给你编译错误(可能是警告,是的)。

Of course, the moment you start adding a lot of this if s, you should start thinking about creating an Html Helper that will render the right Partial based on the type. 当然,当你开始添加大量的这个if ,你应该开始考虑创建一个Html Helper,它将根据类型呈现正确的Partial。

Even better, you may be able to use some kind of convention and do this: 更好的是,您可以使用某种约定并执行此操作:

@Html.Partial("_" + Model.GetType().Name + "Form", Model)

Disclaimer: not tested so may be a bit more problematic than it seems. 免责声明:未经测试,可能会比看起来更有问题。 :) :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM