示例:
IApple
和实施Apple
.Apple
的构造函数:
public Apple(IVitamin vitamin, int size)
我可以注册所有 DI 和 IApple:
container.RegisterType<IApple,Apple>();
container.RegisterInstance<IVitamin>(vitamin);
我现在可以在创建 apple 实例以插入 int size 参数时覆盖参数:
var apple = container.Resolve<IApple>(new ParameterOverrides<Apple> {{"size", 9001}}
您必须在其中编写参数的字符串("size")似乎很麻烦。当涉及其他参数时,这是执行 DI 的首选方法吗?还是我必须创建一个AppleFactory(或一般的工厂)来处理这个问题?(必须为每个具有非 DI 属性和 DI 属性的类编写一个工厂似乎有点过分了。
还是不应该手动覆盖和设置属性?
var apple = container.Resolve<IApple>();
apple.Size = 9001;
这样,代码逻辑将从属性的构造函数转移到 setter。
您必须在其中写入参数的字符串("size")似乎很麻烦
是的,它很丑陋,但没有办法绕过它,至少在 Unity 中是这样。
必须为每个具有非 DI 属性和 DI 属性的类编写一个工厂似乎有点过分了
没错,但这可能是你应该做的。大多数代码不应直接依赖于容器。
如果你做一个工厂,参数覆盖并不那么丑陋,因为你可以使用nameof
而不是字符串:
class AppleFactory : IAppleFactory
{
...
public IApple CreateApple(int size)
{
return _container.Resolve<IApple>(new ParameterOverrides<Apple> {{nameof(size), size}};
}
}
另一种方法是使用我的 Unity.Extras.AutoFactory 扩展,但请记住它处于 alpha 状态......
我建议想想你为什么要以这种方式在类中注入integer
。如果使用 unity 实例化的每个Apple
都被注入相同的值,则该值是常量吗?如果是,您实际上不需要注入它。它可以只是Apple
的一部分。但是在您的示例中,如果Apple
表示具有状态的实体或对象并且可以具有不同的大小,则Factory
绝对是更好的选择。Factory
最终将在初始化期间设置属性的值。如果您正在测试代码,您将帮助自己将这些类型的决策移动到Factory
中,并使 IoC 容器配置专注于构建依赖树,而不是处理对象初始状态的值。
我认为你的例子太简单了。如果您要创建不同大小的苹果,则可能不会从容器中解析它们(通过容器创建苹果的方式有点奇怪,它看起来更适合factory
然后container
)。
不要忘记,容器的主要目的是连接复杂的对象组合/依赖项,这些组合/依赖项应该能够从容器的当前状态创建。当你发现自己为 Resolve 方法提供参数时,我认为你应该停下来并尝试避免它,可能是一些重新设计,重构是正确的方向。
旁注:
在大多数情况下,当我需要注入原语时,我会使用这样的结构/类
public struct AppleSize {
public int Value { get; }
.....
}
现在我可以在container
中注册AppleSize
实例 苹果构造函数将使用AppleSize
而不是int
。避免注册基元。最好使用更具描述性的结构/类定义。
如果场景更复杂(要注入的原语可能取决于太多参数并且需要计算/操作),那么我会创建factory
来封装与对象创建相关的任何操作,我认为这更接近您的场景。