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