将 DI 与构造函数参数结合起来?

发布于 2024-11-28 04:56:26 字数 220 浏览 2 评论 0原文

如何将构造函数注入与“手动”构造函数参数结合起来? IE。

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

其中 IService 应由我的 DI 容器解析/注入,并且应指定 someValue。我如何混合两者?

How do I combine constructor injection with "manual" constructor parameters? ie.

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

Where IService should be resolved/injected by my DI container, and someValue should be specified. How do I mix the two?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

影子的影子 2024-12-05 04:56:26

应尽可能避免此类构造。因此,问问自己:这个参数真的需要作为构造函数参数吗?或者,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();

Such constructs should be avoided whenever possible. Therefore, ask yourself: is this parameter really required as constructor argument? Or can SomeObject be replaced by a stateless one which is reused by everyone that depends on it by passing the parameter to the method you execute on the object?

e.g. Instead of

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;
    }
}

use

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

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

If it is required go for a factory:

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

Preview:
Ninject 2.4 won't require the implementation anymore but allow

kernel.Bind<ISomeObjectFactory>().ToFactory();  // or maybe .AsFactory();
默嘫て 2024-12-05 04:56:26

你真的不应该尝试为此使用 DI。您可以想出各种古怪的解决方案,但它们可能在未来没有意义。

我们的方法是通过 DI 创建一个工厂,然后工厂的 Create 方法将使用传入的 DI 容器构建自身。我们不必经常使用这种模式,但当我们这样做时,它实际上会使产品变得更加干净(因为它使我们的依赖关系图更小)。

You really shouldn't try to use D.I. for this. You could come up with all types of wacky solutions, but they may not make sense down the road.

Our approach is to create a factory via D.I., and the factory's Create method would then build itself out using the passed in D.I. container. We don't have to use this pattern often, but when we do it actually makes the product much cleaner (since it makes our dependency graphs smaller).

压抑⊿情绪 2024-12-05 04:56:26

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

Another approach - initialization in two steps (not ninject related, any DI framework):

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;
    }
}

and usage:

public static class TestClass
{
    public static void TestMethod(IService service)
    {
        //var someObject = new SomeObject(service, 5f);
        var someObject = new SomeObject(service).Load(5f);
    }
}
吃不饱 2024-12-05 04:56:26

我不确定这是一个好的做法,但可以用不同的方式解决,如果您为参数创建一个接口,然后使用您需要的值(或从某处获取)实现该接口的类。这样 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
 ...

I am not sure this is a good practice, but it could be solved in a different way, If you create an interface for the parameters, then a class that implements the interface with the values that you need (or fetch from somewhere). That way DI works with those parameters as well.

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
 ...
懵少女 2024-12-05 04:56:26

我可能会使用一个简单的解决方案来解决这个问题。如果您在需要时知道 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);
    }
}

你可以尝试这样的模式。

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

I would probably use a naive solution to this. If you know the value of someValue when you need it I would remove it from the constructor and add a property to your object so you can set someValue. This way you can get your object from your container and then set the value when you have the object.

My other suggestion is that you instead of accessing it directly you create a factory that you can use to create such object. Then you register the factory in your container and use the factory to create your instance. Something like this:

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

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

you could try a pattern like that.

UPDATE: Updated the code to reflect improvement comments.

梅倚清风 2024-12-05 04:56:26

如果“somevalue”始终是常量,那么您可以在向容器注册类型时考虑使用 InjectionParameters,如下面的文章中所述

请参阅此处

,但如果情况并非如此,则在解析实例时无法指定参数值,您可能会考虑移动来自构造函数的“someValue”,并将其作为类的属性。

If 'somevalue' is always constant then you can think of using InjectionParameters while you are register your type with the container as it explained in the below post

See Here

but if that is not true, than there is no way to sepcify a parameter value while resolving a instance , you may think of moving the 'someValue' from the constructor and make it a property of the class.

寂寞美少年 2024-12-05 04:56:26

在您已标记的 NInject 中,您可以使用 FuncModule 如本文所述

autofac 中也提供了这种方法。

各种工厂方法方法都在这个问题的答案。

编辑:注意虽然这可能很有趣,但请使用@Remo Gloor的解决方案(并且重要的是避免使用这种性质的解决方案的建议)

In NInject, which you have tagged this with, you inject an automatically-generated Factory in the form of a Func<parameters you wish to feed in,T>, using the FuncModule as described in this post.

This approach is also available in autofac for one.

The various Factory method approaches are covered in the answers to this question.

EDIT: NB While this may be entertaining, please use @Remo Gloor's solution (and critically the advice re avoiding a solution of this nature)

つ可否回来 2024-12-05 04:56:26

这不正是 DI\Container::make() 是为了?

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

Isn't this exactly what DI\Container::make() is for?

$object = $container->make(SomeObject::class, ['someValue' => 0.1]);
浸婚纱 2024-12-05 04:56:26

我使用的方法是将这些参数定义为抽象属性。然后创建一个新的子类。它类似于 AndersK 方法。这不是一个解决方案,但它适合我的需求:

而不是

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

你可以做

public class SomeObject
{
    protected abstract Float MyFloat { get; }

    public SomeObject(IService service)
    {
        // You can use MyFloat in this class
    }
}

public class SomeUsableObject: SomeObject
{
    protected override Float MyFloat => 12.5f;

    public SomeUsableObject(IService service):base(service)
    {
    }
}

The approach I used was to define those parameters as abstract properties. And then create a new child class. It is similar to AndersK approach. It is not a go to solution but it suited my needs:

Instead of

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

You can do

public class SomeObject
{
    protected abstract Float MyFloat { get; }

    public SomeObject(IService service)
    {
        // You can use MyFloat in this class
    }
}

public class SomeUsableObject: SomeObject
{
    protected override Float MyFloat => 12.5f;

    public SomeUsableObject(IService service):base(service)
    {
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文