[英]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.