一个依赖注入(DI)容器就是这样的。它是一个框架用于促进DI。您不需要传递容器来解决对象的实例,只需在类的构造函数中请求所需的类型,DI框架将注入适当的依赖项。
Mark Seemann写了一本关于依赖注入的好书,我会推荐它:《.NET中的依赖注入》。
您需要在组合根中注册需要解决的所有内容。也就是说,当您的程序启动时,应该注册所有内容。
假设我们有以下代码:
public class MyClass
{
public Run()
{
var dependency = new Dependency1();
dependency.DoSomething();
}
}
public class Dependency1
{
public void DoSomething()
{
var dependency = new Dependency2();
dependeny.DoSomethingElse();
}
}
public class Dependency2
{
public void DoSomethingElse()
{
}
}
这给我们带来了上述的依赖链: MyClass -> Dependency1 -> Dependency2。
我们首先需要重构类,通过它们的构造函数获取它们的依赖关系,并依赖于接口而不是具体实现。除非有注入点(构造函数、属性等),否则无法注入依赖项。
以下是重构后的代码:
public interface IMyClass
{
void Run();
}
public interface IDependency1
{
void DoSomething();
}
public interface IDependency2
{
void DoSomethingElse();
}
public class MyClass : IMyClass
{
public readonly IDependency1 dep;
public MyClass(IDependency1 dep)
{
this.dep = dep;
}
public void Run()
{
this.dep.DoSomething();
}
}
public class Dependency1 : IDependency1
{
public readonly IDependency2 dep;
public MyClass(IDependency2 dep)
{
this.dep = dep;
}
public void DoSomething()
{
this.dep.DoSomethingElse();
}
}
public class Dependency2 : IDependency2
{
public void DoSomethingElse()
{
}
}
你会注意到现在所有的类都通过它们的构造函数来获取它们的依赖项,而不需要新创建任何东西。类只应该接受它们实际需要的依赖项。例如,MyClass 不需要一个 Dependency2,因此它不会请求一个。它只请求一个 Dependency1,因为那是它所需的全部。Dependency1 需要 Dependency2,而不是 MyClass。
现在,为了将它们全部连接起来,我们可以在组合根中直接创建它们:
void Main()
{
var myClass = new MyClass(new Dependency1(new Dependency2()));
}
如果我们有很多类和依赖关系,你可以看到会变得非常复杂。这就是为什么我们使用容器的原因。它为我们处理了所有依赖图。使用容器后,我们将重写如下:
void Main()
在第二个示例中,容器将处理所有依赖项的连接。因此,我们不需要将Depedency2传递给MyClass,然后再传递给Depedency1。我们只需要在Dependency1中请求它,容器会像第一个示例中那样为我们连接它。
因此,在您的示例中,我们应该这样重写:
static void Main(string[] args)
{
var game = new UnityContainer();
game.RegisterType<IGame, TrivialPursuit>();
game.RegisterType<IAnotherClass, AnotherClass>();
game.RegisterType<IYetAnotherClass, YetAnotherClass>();
var gameInstance = game.Resolve<IGame>();
}
public class Game : IGame
{
public Game(IAnotherClass anotherClass)
{
}
}
public class AnotherClass : IAnotherClass
{
public AnotherClass(IYetAnotherClass yetAnotherClass)
{
}
}
public class YetAnotherClass : IYetAnotherClass {}
在这些情况下,无需在容器之间传递。你可以将依赖项注册到容器中,然后在类构造函数中请求它们。如果你想在类中使用容器而不是通过构造函数请求它,则你并没有进行依赖注入,你只是将容器用作单例服务定位器。这通常应该避免。
容器作为服务定位器
这通常应该避免,但如果你想将容器用作服务定位器,你有两个选择:
1)通过构造函数将容器传递到需要它的类中。
您可以使用上面的示例来连接DI所需的类。但是,在构造函数中不是像IDependency一样请求一个依赖项,而是传递容器。
public class Game : IGame
{
public Game(IContainer container)
{
var blah = container.Resolve<IBlah>();
}
}
2)通过一个静态类请求您的容器:
public static class ServiceLocator
{
private static IContainer container;
public static IContainer Container
{
get
{
if (container == null)
{
container = new Container();
}
return container;
}
}
}
在您的组合根中使用ServiceLocator类将所有内容注册为正常内容。然后要使用:
public class MyClass
{
public void DoSomething()
{
var blah = ServiceLocator.Container.Resolve<IBlah>();
}
}