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

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

class Animal {} class Cat: Animal {} class Dog : Animal { } interface IAnimalHandler where T: Animal { void Handle(T animal); } class AnimalHandler : IAnimalHandler, IAnimalHandler { 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 { new Cat(), new Dog() }; animals.ForEach(a => ah.Handle(a)); 

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

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

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

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

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

 class Dog : Animal { } class Cat : Animal { } interface IHandler { void Handle(T eval); } class DogHandler : IHandler { public void Handle(Dog eval) { // do whatever } } class CatHandler : IHandler { public void Handle(Cat eval) { // do whatever } } 

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

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

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

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

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

完全是你的代码,但使用reflection:

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

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

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

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

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

你传递给Handle()的动物并不重要,因为从Animalinheritance的任何东西都必须有适当的实现才能工作,编译器会因为你的Animal基类中的抽象方法声明而知道这一点。

由于您使用的是.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 { void Handle(IEnumerable eval); } class AnimalHandler : IAnimalHandler where T : IAnimal { public void Handle(IEnumerable eval) { foreach (var t in eval) { Console.WriteLine(t.Name); } } } List dogs = new List() { new Dog() { Name = "Bill Murray" } }; List cats = new List() { new Cat() { Name = "Walter Peck" } }; AnimalHandler  animalHandler = new AnimalHandler(); animalEvaluator.Handle(dogs); animalEvaluator.Handle(cats); 

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

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