繁体   English   中英

Ninject-管理泛型的不变性?

[英]Ninject - Managing Inconvariance of generic types?

给定以下简化的接口/类结构,使用Ninject加载我的通用类型实现时遇到麻烦。

public interface IEntry {}

public class TestEntry : IEntry {}

public interface IDBConnection<T> {}

public class DBConnection<T> : IDBConnection<T> where T : IEntry {}

我在加载的NinjectModule中使用绑定:

Bind<IEntry>().To<TestEntry>();
Bind(typeof(IDBConnection<>)).To(typeof(DBConnection<>));

我想通过以下调用获取DBConnection<TestEntry>的实例:

Kernel.TryGet<IDBConnection<IEntry>>();

但是,这仅返回DBConnection<IEntry> 的开放实例类型 如果将我的Kernel.Get调用更改为:我已经能够返回DBConnection<TestEntry>的实例。

Kernel.TryGet<IDBConnection<TestEntry>>();

我知道泛型是不变的,但是如果我需要规定泛型类的实现以便Ninject加载它,那么我们似乎绕过了DI / IOC的全部目的...所以我必须是绑定,获取或理解事情不正确。

此外,我尝试了另一种绑定/加载方法:

Bind<IEntry>().To<TestEntry>();
Bind(typeof(IDBConnection<IEntry>)).To(typeof(DBConnection<TestEntry>));

Kernel.TryGet<IDBConnection<IEntry>>();

但是,这会产生异常:

System.InvalidCastException:无法将类型为“ DBConnection 1[TestEntry]' to type 'IDBConnection对象转换为类型1[TestEntry]' to type 'IDBConnection 1 [IEntry]”的对象。

这是因为通用类型IDBConnection<IEntry>DBConnection<TestEntry>不协变,对吗?

我希望能够将DBConnection<TestEntry>注入我的IDBConnection<IEntry>声明中以供使用; 但是,泛型的不变性似乎不允许这样做。 有什么解决方案?

编辑:这是一个单元测试,以演示/解释

    public interface IEntry { }

    public class TestEntry : IEntry { }

    public interface IDBConnection<T> where T : IEntry { }

    public class DBConnection<T> : IDBConnection<T> where T : IEntry { }

    class TestModule : NinjectModule
    {
        public override void Load()
        {
            Bind<IEntry>().To<TestEntry>();
            Bind(typeof(IDBConnection<IEntry>)).To(typeof(DBConnection<TestEntry>));
        }
    }

    [Test]
    public void NinjectGenericLoadTest()
    {
        /// this loads the expected type from interfaces however is useless
        /// since loaded against a "var" 
        ///(runtime casts knowing the impl would be required to use)
        StandardKernel kernel = new StandardKernel(new TestModule());
        var ninjected = kernel.TryGet(typeof(IDBConnection<IEntry>));
        Assert.IsInstanceOf<DBConnection<TestEntry>>(ninjected);

        /// The following is what I want but it won't compile 
        ///:"Cannot implicitly convert type 'object' to 
        ///'EasyMongo.Contract.IReader<EasyMongo.Contract.IEasyMongoEntry>'. 
        /// An explicit conversion exists (are you missing a cast?)"
        //kernel = new StandardKernel(new TestModule());
        //IDBConnection<IEntry> ninjectedInterface = kernel.TryGet(typeof(IDBConnection<IEntry>));
        //Assert.IsInstanceOf<DBConnection<Entry>>(ninjectedInterface);

        /// this throws System.InvalidCastException : Unable to cast object of type 
        /// 'DBConnection`1[EasyMongo.Test.Base.RandomTest+Entry]' 
        /// to type 'IDBConnection`1[EasyMongo.Test.Base.RandomTest+IEntry]'.
        /// this is due to incovariance of generic types such that DBConnection<Entry> 
        /// is not a IDBConnection<IEntry> 
        IDBConnection<IEntry> ninjectedInterface = (IDBConnection<IEntry>)kernel.TryGet(typeof(IDBConnection<IEntry>));
        Assert.IsInstanceOf<DBConnection<TestEntry>>(ninjectedInterface);
    }

Ninject将始终返回您要求的类型。 如果您要求IDBConnection<IEntry>那么如果您要求IDBConnection<TestEntry> ,则将获得该类型。 没有超级逻辑可以分析您的代码并为您提供与您要求的类型不同的类型。

但是,直接要求诸如IDBConnection之类的东西还是使用Ninject的错误方法。 您应该使用构造函数注入来注入它:

 public class NeedDbConnection<T> {
      public NeedDbConnection(IDBConnection<T> connection) { ... } 
 }

这样,您将获得适合该类的特定数据库连接。

暂无
暂无

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

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