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

时间:2015-11-25 12:02:19

标签: c# generics dependency-injection ninject inversion-of-control

我有一个具有特定实现的通用接口,如下所示:

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 ...
}

1 个答案:

答案 0 :(得分:2)

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接口,或者您需要使用反射或动态类型。使用非通用接口可能如下所示:

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

当然,缺点是您会失去编译时支持,但如果这两行代码是应用程序中唯一一个像这样调用兽医的行,我会说没问题。您可以轻松添加检查此代码的单元测试。

请注意,in Vet<in T>上的Dog关键字可能无用,除非您在GoldenRetrieverGoldenRetriever都有兽医实施,并希望能够应用Vet<Dog>import UIKit import GoogleMaps class DemoViewController: UIViewController,GMSPanoramaViewDelegate { override func viewDidLoad() { super.viewDidLoad() let panoramaNear = CLLocationCoordinate2DMake(50.059139, -122.958391) let panoView = GMSPanoramaView.panoramaWithFrame(CGRectZero, nearCoordinate:panoramaNear) self.view = panoView; } }