依赖倒置原则、IoC容器和依赖注入:第五部分






4.80/5 (7投票s)
什么是UnityContainer以及注册依赖的不同方式
引言
这是我关于依赖倒置原则和依赖注入的文章的第五部分,也是最后一部分。在上一部分中,我实现了一个功能非常有限的自定义容器,可以用于注册依赖并在需要依赖实例时解析。现在有很多工具可以实现依赖注入,例如UnityContainer、StructureMap、Windsor Castle、NInject等等。在这一部分,我将解释UnitContainer
提供的不同功能,以及如何注册依赖和解析已注册的依赖。
如果您是直接访问本部分的读者,可能会发现代码难以理解。因此,在开始阅读本文之前,请先浏览前面的部分。为了方便导航到之前的文章,我在下面提供了链接:
- 第一部分:依赖倒置原则
- 第二部分:控制反转和IoC容器
- 第三部分:自定义IoC容器
- 第四部分:带生命周期选项的自定义IoC容器
- 第五部分:使用Microsoft Unity进行依赖注入(DI)(当前阅读)
概述
Unity Container (Unity) 是一个功能齐全、可扩展的依赖注入容器。它有助于构建松耦合的应用程序,并为开发人员提供以下优势:
- 简化的对象创建,特别是对于分层对象结构和依赖项。
- 抽象化需求;这允许开发人员在运行时或配置文件中指定依赖项,并简化横切关注点的管理。
- 通过将组件配置推迟到容器来增加灵活性。
- 服务定位功能;这允许客户端存储或缓存容器。
- 实例和类型拦截。
- 按约定注册。
来自[https://github.com/unitycontainer/unity]
依赖注册
注册依赖项可以让IoC容器知道依赖项的具体实现。例如,如果我们想在请求解析IReader
依赖项时获取KeyboardReader
的实例,我们就需要注册一个解析KeyboardReader
类实例的IReader
依赖项。以下是使用Unity容器注册依赖项的示例。
IUnityContainer container = new UnityContainer();
container.RegisterType<IReader, KeyboardReader>();
通过上述注册,在解析类型IReader
[container.Resolve<IReader>()
]时,将返回KeyboardReader
的一个实例。
UnityContainer支持以下几种依赖注册方式:
实例注册
通过实例注册,我们告诉IoC容器,当有人请求解析依赖项时,将返回已注册的实例。当您想以特定状态注册依赖项时,实例注册非常有用。RegisterInstance()
方法用于在容器中注册实例。
var keyboardReader = new KeyboardReader();
container.RegisterInstance(keyboardReader);
通过上述注册,在解析类型KeyboardReader
[container.Resolve<KeyboardReader>()
]时,将返回已注册的实例。
实例可以与名称一起注册到IoC容器中。示例如下:
var keyboardReaderA = new KeyboardReader();
var keyboardReaderB = new KeyboardReader();
container.RegisterInstance("InstanceA", keyboardReaderA);
container.RegisterInstance("InstanceB", keyboardReaderB);
通过上述注册,在解析类型KeyboardReader
时,我们需要提供名称,因为不存在没有名称的注册。因此,在上述情况下,通过container.Resolve(KeyboardReader, "InstanceA")
解析类型KeyboardReader
将返回keyboarderReaderA
实例,而通过container.Resolve(KeyboardReader, "InstanceB")
解析类型KeyboardReader
将返回keyboardReaderB
。
实例注册也支持类型映射。这意味着一个实例可以 against 一个类型(类或接口)注册,以便在解析该类型时,将已注册的实例返回给调用者。示例如下:
var keyboardReader = new KeyboardReader();
container.RegisterInstance<IReader>(keyboardReader);
通过上述注册,在解析类型KeyboardReader
[container.Resolve<IReader>()
]时,将返回已注册的keyboardReader
实例。
工厂注册
通过工厂注册,我们可以基于函数调用的输出注册依赖项。这在注册实例时提供了更多的控制。
var keyboardReader = new KeyboardReader();
container.RegisterFactory<IReader>(fn => new KeyboardReader());
类型注册
类型注册是最常用的注册方式。在这种类型的注册中,我们需要提供契约类型和具体类型,其中解析契约类型将产生具体类型的实例。类型注册的最低要求是类型本身。以下是注册的示例:
container.RegisterType<KeyboardReader>();
通过上述注册,在解析类型KeyboardReader
[container.Resolve<KeyboardReader>()
]时,将返回KeyboardReader
的一个实例。
以下是另一个注册示例,其中我们可以提供契约类型及其具体类型。
container.RegisterType<IReader, KeyboardReader>(); // default registration
通过上述注册,在解析类型KeyboardReader
[container.Resolve<IReader>()
]时,将返回KeyboardReader
的一个实例。
我们也可以在注册类型时提供名称。
container.RegisterType<IReader, KeyboardReader>("Keyboard");
通过上述注册,要解析依赖项,我们需要使用container.Resolve<IReader>("Keyboard")
。
注意:名称的默认值为null
。当我们注册一个类型而不传递任何名称时,这种注册称为默认注册。
示例应用
让我们使用Visual Studio创建一个控制台应用程序。在这个演示应用程序中,我使用了Visual Studio 2019。但是您可以使用任何版本的Visual Studio(从Visual Studio 2012开始)。
右键单击项目,然后单击“管理NuGet程序包…”。管理NuGet程序包用于将第三方库添加到应用程序中。
在“管理NuGet程序包”窗口中,选择浏览选项,搜索Unity并安装该程序包。在撰写本文时,Unity的最新版本是5.11.6。但是您可以使用任何稳定版本用于演示应用程序。
在Program.cs文件中添加以下代码行:
using System;
using Unity;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
IUnityContainer container = new UnityContainer();
container.RegisterType<IReader, KeyboardRader>();
var reader = container.Resolve<IReader>();
var readText = reader.Read();
Console.WriteLine($"The output is : {readText}");
Console.ReadLine();
}
}
interface IReader
{
string Read();
}
class KeyboardRader : IReader
{
public string Read()
{
return "Reading from keyboard...";
}
}
}
您的示例应用程序已准备好运行。按F5运行控制台应用程序。
您将获得以下输出:
正如您所注意到的,我们已经通过类型注册将IReader
类型注册为KeyboardReader
具体类型。因此,当我们尝试解析IReader
类型时,我们将获得KeyboardReader
的实例。调用Read()
方法将返回“Reading from keyboard...”,并显示在控制台上。
UnityContainer提供了更多功能,我将在下一篇文章中介绍。此外,我将向您展示注入依赖项的不同方法。
摘要
在本篇文章中,我解释了UnityContainer是什么,如何注册依赖项,并创建了一个演示应用程序来展示使用UnityContainer注册和解析依赖项。
历史
- 2020 年 4 月 23 日:初始版本