7

让我在以下示例中向您解释我要解决的问题:

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

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

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

4

7 回答 7

8

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

var ah = new AnimalHandler();
var animals = new List<Animal> { new Cat(), new Dog() };
animals.ForEach(a => ah.Handle((dynamic)a));
于 2012-07-17T19:39:51.023 回答
5

对我来说,听起来您可以从这种模式中受益(使用 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);
}

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

于 2012-07-17T17:32:57.113 回答
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 });
});
于 2012-07-17T17:37:39.293 回答
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));
于 2012-07-17T17:37:26.813 回答
0

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

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

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

传递给 Handle() 的动物无关紧要,因为从 Animal 继承的任何东西都必须具有正确的实现才能工作,并且编译器会因为 Animal 基类中的抽象方法声明而知道这一点。

于 2012-07-17T17:23:51.610 回答
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);
于 2012-07-17T17:25:14.637 回答
0

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

于 2012-07-17T17:18:44.560 回答