让我在下面的例子中解释一下我正在解决的问题:
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的一部分。将它们视为纯粹的纯域对象,我不希望它们知道任何类型的处理程序
答案 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));