使用 Unity C# 将父项注入复合构造函数

Injecting parents into composite constructors with Unity C#

我正在尝试让 IoC 在 C# 中与 Unity 一起工作,并希望将 wrapper/composite class 传递给子项。

组成多个 classes 的顶级 class 提供了组成的 classes 需要访问的一些通用功能。

举例说明:

// The top composite class
public class Context : IContext {
  public ISomething SomethingProcessor { get; }
  public IAnother AnotherProcessor { get; }

  public Context(ISomething something, IAnother another) {
    this.SomethingProcessor = something;
    this.AnotherProcessor = processor;
  }

  // A function that individual classes need access to, which itself calls one of the children.
  public string GetCommonData() {
    return this.AnotherProcessor.GetMyData();
  }
}

public class Something : ISomething {
  private _wrapper;
  public Something(IContext context) {
    this._wrapper = context;
  }

  // This class has no knowledge of IAnother, and requests data from the master/top class, which knows where to look for whatever.
  public void Do() {
    Console.WriteLine(_wrapper.GetCommonData());
  }
}

public class Another : IAnother {
  public string GetMyData() {
    return "Foo";
  }
}

如果您没有使用 IoC,这很容易,因为 Context class 的构造函数变为:

public Context() {
  this.SomethingProcessor = new Processor(this);
  this.AnotherProcessor = new Another();
}

但是当你使用 IoC 时,"this" 的想法还不存在,因为它还没有被注入器构造。相反,您拥有的是循环依赖。

container.RegisterType<ISomething, Something>();
container.RegisterType<IAnother, Another>();
container.RegisterType<IContext, Context>();

var cxt = container.Resolve<IContext>();  // WhosebugException

上面的例子已经被大大简化以说明这个概念。我正在努力寻找 "best practice" 处理这种结构以启用 IOC 的方法。

我不知道这个模式的名称,也不知道它是好是坏,但是您可以通过创建一个绑定 [=35= 的方法来解决 "double-binding" 的问题],而不是在构造函数中进行。

例如,

1) ISomething 有一个 void BindContext(IContext context) 方法

2) 你这样实现它:

class Something : ISomething 
{
   IContext _wrapper;

   // ... nothing in constructor

   public void BindContext(IContext context)
   {
        _wrapper = context;
   }
}

3) 删除 Something 构造函数中的 IContext 依赖注入。

然后您从上下文构造函数中调用它:

public Context(ISomething something, IAnother another) {
    this.SomethingProcessor = something;
    this.SomethingProcessor.BindContext(this);

    // same for IAnother
}

你也为 IAnother 做同样的事情。您甚至可以提取一些通用接口 "IBindContext" 使事情变得更精彩 "DRY"(不要重复自己)并使 IAnotherISomething 继承它。

未测试,再次:不确定这是进行此类依赖关系设计的最佳方式。如果有另一个答案可以提供最先进的见解,我会很高兴。

工厂模式是一种基于其他依赖项或逻辑选择构造对象的方法。

Factory Method: "Define an interface for creating an object, but let the classes which implement the interface decide which class to instantiate. The Factory method lets a class defer instantiation to subclasses" (c) GoF.

Lots of construction.. hence the name Factory Pattern

可以与 DI 一起使用的原始代码示例

public class ContextFactory : IContextFactory {

  _anotherProcessor = anotherProcessor;

 public ContextFactory(IAnotherProcessor anotherProcessor) {
     //you can leverage DI here to get dependancies
 }

 public IContext Create(){
    Context factoryCreatedContext = new Context();

    factoryCreatedContext.SomethingProcessor = new SomethingProcessor(factoryCreatedContext )
    factoryCreatedContext.AnotherProcessor = _anotherProcessor;

    //You can even decide here to use other implementation based on some dependencies. Useful for things like feature flags.. etc.

    return context;
  }

}

你可以摆脱这个,也许吧? - 但这里仍然存在循环引用问题,我永远不会提交这种代码。

这里的问题你需要专注于Inversion Of Control GetCommonData

您的 SomethingProcessor 不应依赖另一个 class 中的方法。这是可以使用 In Inheritance 的地方,但是 Inheritance 很快就会变得非常复杂。

最好的方法是确定两个或许多其他地方都需要的一件事,并将其分解为一个新的依赖项。这就是您反转控制的方式。

提示:

不要过度使用接口 - 在您认为将使用多态性的地方使用接口,例如必须向您保证它们已实现特定 method/property 的不同对象的集合。否则,您将过度使用接口并增加复杂性。 DI 不必使用接口,它可以是具体的实现。存储库上的接口很有用,因为您可以轻松切换数据库,但实际上并不需要像这样的工厂接口。