.NET泛型:如何在运行时解析类型T?

时间:2012-07-17 17:13:23

标签: c# .net c#-4.0 generics

让我在下面的例子中解释一下我正在解决的问题:

class Animal {}
class Cat: Animal {}
class Dog : Animal { }

interface IAnimalHandler<in T> where T: Animal
{
    void Handle(T animal);
}

class AnimalHandler : 
    IAnimalHandler<Cat>,
    IAnimalHandler<Dog>
{
    public void Handle(Cat animal)
    {
        Console.Write("it's a cat !");
    }

    public void Handle(Dog animal)
    {
        Console.Write("it's a dog !");
    }
}

所以现在我想要通过所有动物并运行这样的适当处理程序:

  var ah = new AnimalHandler();
  var animals = new List<Animal> { new Cat(), new Dog() };
  animals.ForEach(a => ah.Handle(a));

但是这段代码不起作用(无法解析方法Hanler&lt;&gt; ... )只是因为.NET编译器需要在编译之前知道这里使用了哪种类型,所以可能是什么这个问题的最佳解决方案?换句话说,我需要让.NET编译器为运行时中T 类型的每个实例采用类型为T的适当处理程序。 我不想使用多个if语句检查实例类型。

更新:很抱歉错过了它,这对我来说似乎很明显,但现在我明白它并不那么明显:AnimalHandler类包含的逻辑不应该是域对象Cat和Dog的一部分。将它们视为纯粹的纯域对象,我不希望它们知道任何类型的处理程序

7 个答案:

答案 0 :(得分:8)

您可以使用C#4 dynamic将重载决策步骤从编译时移到运行时:

var ah = new AnimalHandler();
var animals = new List<Animal> { new Cat(), new Dog() };
animals.ForEach(a => ah.Handle((dynamic)a));

答案 1 :(得分:5)

对我来说,听起来你可以从this pattern中受益(使用StructureMap实现)。从原始语句开始,“我需要让.NET编译器为运行时类型为T的每个实例采用类型为T的适当处理程序”它可能看起来像这样:

class Dog : Animal { }
class Cat : Animal { }

interface IHandler<T>
{
    void Handle(T eval);
}

class DogHandler : IHandler<Dog>
{
    public void Handle(Dog eval)
    {
        // do whatever
    }
}

class CatHandler : IHandler<Cat>
{
    public void Handle(Cat eval)
    {
        // do whatever
    }
}    

然后,您可以根据链接文章配置StructureMap,并使用以下命令获取相应的处理程序:

var dogHandler = _container.GetInstance<IHandler<Dog>>(); // instance of DogHandler
var catHandler = _container.GetInstance<IHandler<Cat>>(); // instance of CatHandler

更新: 要在循环中解决这些问题,您可以执行以下操作:

foreach (var animal in animals)
{
    var concreteHandlerType = typeof(IHandler<>).MakeGenericType(animal.GetType());
    var handler = _container.GetInstance(concreteHandlerType);
    handler.Handle(animal);
}

我在一个相当大的系统中使用这种模式来实现相同的目标(纯域对象,不应该在这些域对象内部的逻辑处理程序,简化维护)。它适用于您希望为每个对象都有一个单独的处理程序类的系统。

答案 2 :(得分:1)

正是您的代码,但使用反射:

var ah = new AnimalHandler();
var animals = new List<Animal> { new Cat(), new Dog() };
animals.ForEach(a => {
  var method = ah.GetType().GetMethod("Handle", new Type[] {a.GetType()});
  method.Invoke(ah,new object[] { a });
});

答案 3 :(得分:0)

为什么你会为每种动物配备特定的处理程序。而是实现多个特定接口,只需实现IAnimalHandler<T>,并且只需要一个Handle(T obj)方法。如果您需要特定于类型的功能,可以通过调用typeof(obj)来获取特定类型。

答案 4 :(得分:0)

这是一种方法:在Animal中创建一个抽象方法,例如“BeingHandled()”,然后Animal的所有继承者都必须提供自己的实现。

然后你的AnimalHandler类将有一个Handle(Animal a)方法:

class AnimalHandler
{
    public void Handle(Animal a)
    {
        a.BeingHandled();
    }
}

你传递给Handle()的动物并不重要,因为任何继承自Animal 的东西必须才能正常实现,并且由于你的抽象方法声明,编译器会知道这一点动物基类。

答案 5 :(得分:0)

由于您使用的是.NET 4.0,因此利用协方差/逆变为您的类型注入处理程序。

interface IAnimal
{
    string Name { get; set; }
}

class Dog : IAnimal
{
    public string Name { get; set; }
}

class Cat : IAnimal
{
    public string Name { get; set; }
}

interface IAnimalEvaluator<T>
{
    void Handle(IEnumerable<T> eval);
}

class AnimalHandler : IAnimalHandler<T> where T : IAnimal
{
    public void Handle(IEnumerable<T> eval)
    {
        foreach (var t in eval)
        {
            Console.WriteLine(t.Name);
        }
    }
}


List<Dog> dogs = new List<Dog>() { new Dog() { Name = "Bill Murray" } };
List<Cat> cats = new List<Cat>() { new Cat() { Name = "Walter Peck" } };

AnimalHandler <IAnimal> animalHandler = new AnimalHandler<IAnimal>();

animalEvaluator.Handle(dogs);
animalEvaluator.Handle(cats);

答案 6 :(得分:0)

使用访客模式和双重调度。它的工作原理如下。处理程序可以处理不同类型的动物。动物选择正确的方法,而不是让处理者选择正确的方法。这很容易,因为动物总是需要相同的方法(“他的”方法)。

class Animal
{
    string Name { get; set; }
    abstract public Handle(IAnimalHandler handler);
}

class Cat : Animal
{ 
    public overrides Handle(IAnimalHandler handler)
    {
        handler.Handle(this); // Chooses the right overload at compile time!
    }
}

class Dog : Animal
{ 
    public overrides Handle(IAnimalHandler handler)
    {
        handler.Handle(this); // Chooses the right overload at compile time!
    }
}

interface IAnimalHandler
{
    void Handle(Cat cat);
    void Handle(Dog dog);
}

class AnimalHandler : IAnimalHandler
{
    public void Handle(Cat cat)
    {
        Console.Write("it's cat {0}", cat.Name);
    }

    public void Handle(Dog dog)
    {
        Console.Write("it's dog {0}", dog.Name);
    }
}

现在你可以处理这样的动物了

IAnimalHandler handler = new AnimalHandler();
animals.ForEach(a => a.Handle(handler));

handler = new SomeOtherAnimalHandler();
animals.ForEach(a => a.Handle(handler));