我有一个具有特定实现的通用接口(interface),如下所示:
public class Animal { }
public class Horse : Animal { }
public class Dog : Animal { }
public interface Vet<in T> where T : Animal
{
void Heal(T animal);
}
public class HorseVet : Vet<Horse>
{
public void Heal(Horse animal)
{
Console.WriteLine("Healing a horse");
}
}
public class DogVet : Vet<Dog>
{
public void Heal(Dog animal)
{
Console.WriteLine("Healing a dog");
}
}
现在,我希望能够在运行时给定
Vet<T>
的情况下创建 Animal
的适当具体实现的实例,如下所示:StandardKernel kernel = new StandardKernel();
kernel.Bind<Vet<Horse>>().To<HorseVet>();
kernel.Bind<Vet<Dog>>().To<DogVet>();
var animals = new Animal[] { new Horse(), new Dog() };
foreach (Animal animal in animals)
{
// how do I get the right Vet<T> type here so I can call Heal(animal)?
}
我的问题是,我怎样才能实现上述目标以便检索正确的实现,或者我是否需要重构为不同的组织类的方式?
我看过 Ninject Factory Extension 但这似乎也没有提供我正在寻找的东西。
如果我不使用 IoC 容器,我会执行以下操作:
public Vet<Animal> Create(Animal animal)
{
if (animal is Horse)
{
return new HorseVet();
}
else if ...
}
最佳答案
foreach (Animal animal in animals)
{
Type vetType = typeof(Vet<>).MakeGenericType(animal.GetType());
object vet = kernel.Get(vetType);
}
但是现在的问题变成了您想如何使用这种类型?你可能会想做这样的事情:
var vet = (Vet<Animal>)kernel.Get(vetType);
vet.Heal(animal);
但这不起作用,因为
Vet<Dog>
和 Vet<Horse>
的实例不能转换为 Vet<Animal>
,因为这需要 Vet<T>
像 out
一样用 Vet<out Animal>
关键字定义。但这当然行不通,因为 Vet<T>
有一个 Animal
类型的输入参数。因此,要解决此问题,您要么需要第二个非通用
Vet
接口(interface),要么需要使用反射或动态类型。使用非通用接口(interface)可能如下所示:public interface Vet {
void Heal(Animal animal);
}
public interface Vet<T> : Vet where T : Animal {
void Heal(T animal);
}
// Usage
var vet = (Vet)kernel.Get(vetType);
vet.Heal(animal);
然而问题是这会污染实现,因为他们突然需要实现第二种方法。
另一种选择是使用动态类型或反射:
dynamic vet = kernel.Get(vetType);
vet.Heal((dynamic)animal);
缺点当然是你会失去编译时支持,但是如果这两行代码是应用程序中唯一像这样调用 vet 的行,我会说这很好。您可以轻松添加检查此代码的单元测试。
请注意,
in
上的 Vet<in T>
关键字可能没用,除非您在 Dog
和 GoldenRetriever
上都有 vet 实现并且希望能够将 GoldenRetriever
应用于 Vet<Dog>
。