Dependency injection 是否有用于初始化通过DI容器创建的对象的模式

Dependency injection 是否有用于初始化通过DI容器创建的对象的模式,dependency-injection,inversion-of-control,unity-container,ioc-container,interface-design,Dependency Injection,Inversion Of Control,Unity Container,Ioc Container,Interface Design,我正试图让Unity管理对象的创建,我希望有一些直到运行时才知道的初始化参数: 目前我唯一能想到的方法就是在接口上有一个Init方法 接口IMyIntf{ void初始化(字符串runTimeParam); 字符串RunTimeParam{get;} } 然后使用它(在统一中),我会这样做: var IMyIntf=unityContainer.Resolve(); IMyIntf.Initialize(“somevalue”); 在此场景中,runTimeParamparam在运行时根据用

我正试图让Unity管理对象的创建,我希望有一些直到运行时才知道的初始化参数:

目前我唯一能想到的方法就是在接口上有一个Init方法

接口IMyIntf{
void初始化(字符串runTimeParam);
字符串RunTimeParam{get;}
}
然后使用它(在统一中),我会这样做:

var IMyIntf=unityContainer.Resolve();
IMyIntf.Initialize(“somevalue”);
在此场景中,
runTimeParam
param在运行时根据用户输入确定。这里的小例子只是返回
runTimeParam
的值,但实际上该参数类似于文件名,initialize方法将对文件进行处理

这会产生许多问题,即
初始化
方法在接口上可用,并且可以多次调用。在实现中设置一个标志,并在重复调用
Initialize
时抛出异常,这看起来很笨拙

在解析接口时,我不想知道任何关于
IMyIntf
实现的信息。不过,我真正想要的是知道这个接口需要某些一次性初始化参数。有没有一种方法可以在创建对象时以某种方式用这些信息注释(属性?)接口并将其传递给框架


编辑:更详细地描述了界面。

我无法用具体的Unity术语回答,但听起来您只是在学习依赖注入。如果是这样的话,我强烈要求你阅读简短、清晰、包装齐全的信息

这将引导您了解在使用DI时的各种选择,以及如何说明在使用过程中将面临的具体问题。在您的情况下,您很可能希望使用DI容器来实例化您的对象,并让该对象通过构造函数获得对其每个依赖项的引用

本演练还详细介绍了如何在运行时使用属性对方法、属性甚至参数进行注释以区分它们


即使您不使用Ninject,本演练也会为您提供适合您的目的的功能的概念和术语,您应该能够将这些知识映射到Unity或其他DI框架(或者说服您尝试使用Ninject)。

我认为我已经解决了这个问题,它感觉相当健康,所以它必须是正确的一半:)

我将
IMyIntf
分为一个“getter”和一个“setter”接口。因此:

interface IMyIntf {
  string RunTimeParam { get; }
}


interface IMyIntfSetter {
  void Initialize(string runTimeParam);
  IMyIntf MyIntf {get; }
}
然后实施:

class MyIntfImpl : IMyIntf, IMyIntfSetter {
  string _runTimeParam;

  void Initialize(string runTimeParam) {
    _runTimeParam = runTimeParam;
  }

  string RunTimeParam { get; }

  IMyIntf MyIntf {get {return this;} }
}

//Unity configuration:
//Only the setter is mapped to the implementation.
container.RegisterType<IMyIntfSetter, MyIntfImpl>();
//To retrieve an instance of IMyIntf:
//1. create the setter
IMyIntfSetter setter = container.Resolve<IMyIntfSetter>();
//2. Init it
setter.Initialize("someparam");
//3. Use the IMyIntf accessor
IMyIntf intf = setter.MyIntf;
类MyIntfImpl:IMyIntf,IMyIntfSetter{
字符串_runTimeParam;
无效初始化(字符串runTimeParam){
_runTimeParam=runTimeParam;
}
字符串RunTimeParam{get;}
IMyIntf MyIntf{get{返回此;}
}
//统一配置:
//只有setter映射到实现。
container.RegisterType();
//要检索IMyIntf的实例,请执行以下操作:
//1. 创建setter
imyintsetter setter=container.Resolve();
//2. 初始化它
setter.Initialize(“someparam”);
//3. 使用IMyIntf访问器
IMyIntf intf=setter.MyIntf;

imyintsetter.Initialize()
仍然可以被多次调用,但使用一些函数,我们可以很好地将其封装起来,这样
imyintsetter
几乎就是一个内部接口,通常在遇到这种情况时,它与
IMyIntf

不同,您需要重新审视您的设计,并确定是否将有状态/数据对象与纯服务混合在一起。在大多数(并非所有)情况下,您将希望将这两种类型的对象保持分离

如果确实需要在构造函数中传递特定于上下文的参数,一个选项是创建一个工厂,该工厂通过构造函数解析服务依赖项,并将运行时参数作为create()方法(或Generate()、Build()或任何您命名的工厂方法)的参数


拥有setter或Initialize()方法通常被认为是糟糕的设计,因为您需要“记住”调用它们,并确保它们不会打开太多的实现状态(即,如何阻止某人重新调用Initialize或setter?).

任何需要运行时值来构造特定依赖项的地方,抽象工厂是解决方案

在接口上使用初始化方法有一种泄漏抽象的味道

在您的例子中,我想说的是,您应该根据您需要如何使用它来建模
IMyIntf
接口,而不是您打算如何创建它的实现。这是一个实现细节

因此,接口应该是:

公共接口IMyIntf
{
字符串RunTimeParam{get;}
}
现在定义抽象工厂:

公共接口imyintfactory
{
IMyIntf Create(字符串runTimeParam);
}
您现在可以创建
imyintfactory
的具体实现,该实现创建了
IMyIntf
的具体实例,如下所示:

公共类MyIntf:IMyIntf
{
私有只读字符串runTimeParam;
公共MyIntf(字符串运行时参数)
{
if(runTimeParam==null)
{
抛出新ArgumentNullException(“runTimeParam”);
}
this.runTimeParam=runTimeParam;
}
公共字符串运行时参数
{
获取{返回this.runTimeParam;}
}
}
请注意,这允许我们通过使用
readonly
关键字保护类的不变量。没有臭的初始化方法是必要的

imyintfactory
实现可能非常简单:

公共类MyIntfFactory:IMyIntfFactory
{
公共IMyIntf Create(字符串runTimeParam)
{
返回新的MyIntf(runTimeParam);
}
}
在你所有的时间里