简体   繁体   English

在Ninject中寻找通用接口的具体实现

[英]Finding a Concrete Implementation of a Generic Interface in Ninject

I have a generic interface with specific implementations as follows: 我有一个具有特定实现的通用接口,如下所示:

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

Now I want to be able to create an instance of the appropriate concrete implementation of Vet<T> given an Animal at runtime, as follows: 现在我希望能够在运行时给定Animal创建Vet<T>的适当具体实现的实例,如下所示:

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)?
}

My question is, how can I achieve the above so the correct implementation is retrieved, or do I need to refactor to a different way of organising my classes? 我的问题是,如何实现上述目标以便检索正确的实现,或者我是否需要重构以不同的方式组织我的类?

I've looked at the Ninject Factory Extension but that also doesn't seem to offer what I'm looking for. 我看过Ninject Factory Extension,但似乎也没有提供我正在寻找的东西。

If I wasn't using an IoC container, I'd do something like: 如果我没有使用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);
}

But the question now becomes how do you want to use this type? 但现在的问题是你想如何使用这种类型? You might be tempted to do something like this: 你可能想做这样的事情:

var vet = (Vet<Animal>)kernel.Get(vetType);
vet.Heal(animal);

But this doesn't work, because instances of Vet<Dog> and Vet<Horse> can't be cast to Vet<Animal> , because this would require Vet<T> to be defined with an out keyword as in Vet<out Animal> . 但这不起作用,因为Vet<Dog>Vet<Horse>实例无法转换为Vet<Animal> ,因为这需要使用out关键字定义Vet<T> ,如Vet<out Animal> But this of course wouldn't work, because Vet<T> has an input parameter of type Animal . 但这当然行不通,因为Vet<T>有一个类型为Animal的输入参数。

So to solve this you either need a second non-generic Vet interface, or you need to either use reflection or dynamic typing. 因此,要解决此问题,您需要第二个非通用Vet接口,或者您需要使用反射或动态类型。 Using the non-generic interface might look like this: 使用非通用接口可能如下所示:

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

Problem however is that this pollutes the implementations, because they suddenly need to implement a second method. 然而问题是这会污染实现,因为它们突然需要实现第二种方法。

Another option is to use dynamic typing or reflection: 另一种选择是使用动态类型或反射:

dynamic vet = kernel.Get(vetType);
vet.Heal((dynamic)animal);

Downside of course is that you would lose compile-time support, but if these two lines of code are the only lines in the application that call the vet like that, I would say it is fine. 当然,缺点是您会失去编译时支持,但如果这两行代码是应用程序中唯一一个像这样调用兽医的行,我会说没问题。 You can easily add a unit test that checks this code. 您可以轻松添加检查此代码的单元测试。

Do note btw that the in keyword on Vet<in T> is probably useless, unless you have vet implementations on both Dog and GoldenRetriever and want to be able to apply a GoldenRetriever to a Vet<Dog> . 请注意,即使你在DogGoldenRetriever上都有vet实现,并希望能够将一个GoldenRetriever应用于Vet<Dog> ,那么Vet<in T>中的in关键字可能是无用的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM