簡體   English   中英

將 DI 與構造函數參數結合起來?

[英]Combining DI with constructor parameters?

如何將構造函數注入與“手動”構造函數參數結合起來? IE。

public class SomeObject
{
    public SomeObject(IService service, float someValue)
    {
    }
}

我的 DI 容器應該解析/注入 IService,並且應該指定 someValue。 我如何混合兩者?

應盡可能避免此類構造。 因此,問問自己:這個參數真的需要作為構造函數的參數嗎? 或者可以將 SomeObject 替換為無狀態的,通過將參數傳遞給您在對象上執行的方法,每個依賴它的人都可以重用它?

例如代替

public class SomeObject
{
    private float someValue
    public SomeObject(IService service, float someValue)
    {
        this.someValue = someValue
    }

    public float Do(float x)
    {
        return this.Service.Get(this.someValue) * x;
    }
}

采用

public class SomeObject
{
    public SomeObject(IService service)
    {
    }

    public float Do(float x, float someValue)
    {
        return this.Service.Get(someValue) * x;
    }
}

如果需要去工廠:

public interface ISomeObjectFactory
{
    ISomeObject CreateSomeObject(float someValue);
}

public class SomeObjectFactory : ISomeObjectFactory
{
    private IKernel kernel;
    public SomeObjectFactory(IKernel kernel) 
    {
        this.Kernel = kernel;
    }

    public ISomeObject Create(float someValue)
    {
        return this.kernel.Get<ISomeObject>(WithConstructorArgument("someValue", someValue);
    }
}

預覽:Ninject 2.4 不再需要實現,但允許

kernel.Bind<ISomeObjectFactory>().ToFactory();  // or maybe .AsFactory();

您真的不應該嘗試為此使用 DI。 你可以想出所有類型的古怪解決方案,但它們在未來可能沒有意義。

我們的方法是通過 DI 創建一個工廠,然后工廠的 Create 方法將使用傳入的 DI 容器構建自己。 我們不必經常使用這種模式,但是當我們這樣做時,實際上會使產品更干凈(因為它使我們的依賴圖更小)。

另一種方法 - 分兩步初始化(與 ninject 無關,任何 DI 框架):

public class SomeObject
{
    private readonly IService _service;

    public SomeObject(IService service)
    {
        // constructor only captures dependencies
        _service = service;
    }

    public SomeObject Load(float someValue)
    {
        // real initialization goes here
        // ....

        // you can make this method return no value
        // but this makes it more convienient to use
        return this;
    }
}

和用法:

public static class TestClass
{
    public static void TestMethod(IService service)
    {
        //var someObject = new SomeObject(service, 5f);
        var someObject = new SomeObject(service).Load(5f);
    }
}

我可能會對此使用一個天真的解決方案。 如果您在需要時知道someValue的值,我會將其從構造函數中刪除並向您的對象添加一個屬性,以便您可以設置someValue 通過這種方式,您可以從容器中獲取對象,然后在擁有對象時設置該值。

我的另一個建議是,您不要直接訪問它,而是創建一個可用於創建此類對象的工廠。 然后您在容器中注冊工廠並使用工廠創建您的實例。 像這樣的東西:

public class SomeObjectFactory : ISomeObjectFactory
{
    private IYourService _service;
    public SomeObjectFactory(IYourService service) 
    {
        _service = service;
    }

    public ISomeObject Create(float someValue)
    {
        return new SomeObject(_service, someValue);
    }
}

你可以試試這樣的模式。

更新:更新了代碼以反映改進意見。

我不確定這是一個很好的做法,但可以用不同的方式解決,如果你為參數創建一個接口,那么一個類用你需要的值(或從某處獲取)來實現接口。 這樣 DI 也可以使用這些參數。

interface ISomeParameters
{
  public float SomeValue { get; set; }
}

class SomeParameters : ISomeParameters
{
 public float SomeValue{ get; set; } = 42.0;
}

services.AddSingleton(ISomeParameters, SomeParameters)

public MyService(IService service, ISomeParameters someParameters)
{
  someParameters.SomeValue
 ...

如果 'somevalue' 始終保持不變,那么您可以在向容器注冊類型時考慮使用 InjectionParameters,如下面的帖子中所述

看這里

但如果這不是真的,那么在解析實例時無法分離參數值,您可能會考慮從構造函數中移動“someValue”並使其成為類的屬性。

在 NInject 中,你已經標記了 this,你使用FuncModule 以Func<parameters you wish to feed in,T> want Func<parameters you wish to feed in,T>的形式注入一個自動生成的 Factory, 如本文所述

這種方法也可以在 autofac 中使用。

這個問題的答案涵蓋了各種工廠方法方法

編輯:注意,雖然這可能很有趣,但請使用@Remo Gloor 的解決方案(關鍵是建議避免使用這種性質的解決方案)

這不正是DI\Container::make()的用途嗎?

$object = $container->make(SomeObject::class, ['someValue' => 0.1]);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM