簡體   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