结合DI和构造函数参数?

时间:2011-08-04 03:29:58

标签: c# ninject dependency-management

如何将构造函数注入与“手动”构造函数参数组合?即

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

我的DI容器应该解析/注入IService,并且应该指定someValue。我如何混合这两个?

7 个答案:

答案 0 :(得分:30)

应尽可能避免使用此类构造。因此,问问自己:这个参数真的需要作为构造函数参数吗?或者可以将SomeObject替换为无状态的,通过将参数传递给在对象上执行的方法,每个依赖于它的每个人都可以重用它?

e.g。而不是

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();

答案 1 :(得分:2)

你真的不应该尝试使用D.I.为了这。你可以想出所有类型的古怪解决方案,但在未来可能没有意义。

我们的方法是通过D.I.创建一个工厂,然后工厂的Create方法将使用传入的D.I.自行构建。容器。我们不必经常使用这种模式,但是当我们这样做时,它实际上使产品更清洁(因为它使我们的依赖图更小)。

答案 2 :(得分:2)

另一种方法 - 分两步进行初始化(不是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);
    }
}

答案 3 :(得分:1)

如果'somevalue'始终是常数,那么您可以考虑使用InjectionParameters,同时使用容器注册您的类型,如下面的帖子所述

See Here

但如果不是这样,那么在解析实例时无法分离参数值,您可能会想到从构造函数中移动'someValue'并使其成为类的属性。

答案 4 :(得分:1)

在NInject中,您使用FuncModule as described in this postFunc<parameters you wish to feed in,T>的形式注入自动生成的工厂。

此方法也适用于autofac。

各种Factory method approaches are covered in the answers to this question

编辑:NB虽然这可能很有趣,但请使用@Remo Gloor的解决方案(而且批判性的建议是避免这种性质的解决方案)

答案 5 :(得分:1)

我不确定这是否是一个好习惯,但是可以通过其他方式解决。如果您为参数创建接口,那么一个类将使用所需的值来实现该接口(或从某处获取) )。这样,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
 ...

答案 6 :(得分:0)

我可能会使用一个天真的解决方案。如果您在需要时知道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);
    }
}
你可以尝试这样的模式。

更新:更新了代码以反映改进意见。