Ninject:将接口与也是接口的泛型绑定

时间:2012-09-12 12:21:22

标签: c#-4.0 generics interface ninject-2

我搜索过这个问题,但没有运气。我们走了。

假设我有一个界面:

interface IQueryRepository<T> where T : class

我想绑定任何请求:

IQueryRepository<IClient>

为:

ConcreteQueryRepository<Client>

我已经尝试过显而易见的事了:

Bind<IGenericQueryRepository<IClient>>().To<ConcreteQueryRepository<Client>>()

但是我收到了一个错误:

  

ConcreteQueryRepository<Client>不能用作类型参数&#39; TImplementation&#39;在       通用类型或方法&#39; Ninject.Syntax.IBindingToSyntax<T>.To<TImplementation>()&#39;       来自&#39; ConcreteQueryRepository<Client>&#39;没有隐式参考转化。       到&#39; IGenericQueryRepository<IClient>&#39;

但我不明白为什么,因为GenericQueryRepository实现了IGenericQueryRepository,而Client实现了IClient。

我希望Ninject给我一个具体的通用存储库,其中T是Client。我希望这可以避免在代码中使用具体类型。

可以吗?

2 个答案:

答案 0 :(得分:4)

这与Covariance and Contravariance有关。

在你的问题中,你提到了以下内容:

  

... GenericQueryRepository实现IGenericQueryRepository,Client实现IClient。

让我们通过使用水果简化:水果实施IFruit。我们还将创建一个Tree类。

public interface IFruit { }
public class Fruit : IFruit { }
public class Tree<T> where T : IFruit { }

Tree<IFruit> tree = new Tree<Fruit>() // error

这将重现您遇到的同类错误。为什么?简单。

虽然Fruit实施了IFruit,但果树并没有实施IFruit树。 Fruit Tree和IFruit Tree之间没有任何演员阵容,尽管你会期待它。它们都是树,但具有不同的类型参数。他们的类型参数彼此相关的事实并不重要。

换句话说:Fruit Tree和IFruit Tree之间没有演员阵容,因为他们的类型参数不匹配。

通常,在使用泛型进行投射时,请确保其类型参数匹配。但是,有一些例外情况。请参阅Variance in Generic Interfaces

在您的情况下,您可以使用IClient作为GenericQueryRepository类的类型参数来修复它。执行此操作将允许进行转换,因为类型参数匹配。但我不了解您的应用程序架构,因此在您的情况下此修复可能不适用。


编辑:为了更容易理解,请复制粘贴下面的代码并查看编译器的内容。

interface IFruit { }
class Fruit : IFruit { }
interface ITree<T> where T : IFruit { }
class Tree<T> : ITree<T> where T : IFruit { }

class Program
{
    static void Main(string[] args)
    {
        ITree<Fruit> test1 = new Tree<Fruit>();   // compiles: type parameters match
        ITree<IFruit> test2 = new Tree<Fruit>();  // fails:    type parameters don't match
        ITree<Fruit> test3 = new Tree<IFruit>();  // fails:    type parameters don't match
        ITree<IFruit> test4 = new Tree<IFruit>(); // compiles: type parameters match

        IEnumerable<IFruit> test5 = new List<Fruit>(); // compiles: this is one of the exceptional cases
    }
}

这应该清楚地了解什么是不可能的东西。

答案 1 :(得分:1)

我在尝试将Dapper查询绑定到接口类型时遇到了同样的问题,考虑到它,似乎Dapper无法实例化接口类型。

接口只是一个契约,不知道如何实例化它的具体实现。

Dapper需要一种类型,它是接口类型的具体实现,否则Dapper也必须知道实例化接口的具体实现,在这种情况下,Dapper的行为就像一个DI容器,实际上它不是