首页 > 其他分享 >.NET IoC 容器(二)Unity

.NET IoC 容器(二)Unity

时间:2024-11-08 11:11:06浏览次数:1  
标签:容器 container RegisterType computer0 构造 Unity NET IoC

.NET IoC 容器(二)Unity

1 Unity

Unity Application Block(Unity)是Microsoft模式和实践团队(Patterns & Practices team)推出的一个开源依赖注入容器。它是.NET Framework的一个组件,旨在简化应用程序的构建过程,提高代码的可测试性和可维护性。
Unity Application Block提供了以下功能:

  • 依赖注入(DI):Unity容器允许开发人员将对象的创建和管理交给容器,从而实现对象之间的解耦。通过依赖注入,对象不再负责自己的依赖关系,而是由容器在需要时提供。
  • 对象生命周期管理:Unity容器支持管理对象的生命周期,包括瞬时(transient)、单例(singleton)和线程本地(thread-local)等选项。这使得开发人员可以更精细地控制对象的生存期。
  • 配置灵活性:Unity允许开发人员通过配置文件或代码来定义对象之间的依赖关系和容器的行为。这种灵活性使得应用程序的配置和修改变得更加容易。
  • 支持多种解析方式:Unity容器支持构造函数注入、属性注入和方法注入等多种依赖注入方式,使开发人员可以根据需要选择最适合的解析方式。
  • 可扩展性:Unity容器是可扩展的,开发人员可以通过自定义扩展来增强其功能,以满足特定的需求。

总的来说,Unity Application Block是一个强大的依赖注入容器,可以帮助.NET开发人员构建可测试、可维护和灵活的应用程序。

2 Nuget 安装

19e7fbc5fd41960a979483e3bd12b44b

3 实现DI

3.1 定义接口

internal interface IComputer
{
}
internal interface IKeyboard
{
}
internal interface IMouse
{
}
internal interface IPerson
{
    IComputer Computer { get; set; }
    IKeyboard Keyboard { get; set; }
    IMouse Mouse { get; set; }
    void Work();
}

3.2 定义实现类

internal class ConstructBase
{
    public ConstructBase() 
    {
        Console.WriteLine($"{this.GetType().Name} - 被构造了");
    }
}
internal class LenovoComputer : ConstructBase, IComputer
{
    public LenovoComputer() : base() { }
}
internal class TogarKeyboard : ConstructBase, IKeyboard
{
    public TogarKeyboard() : base() { }
}
internal class LogitechMouse : ConstructBase, IMouse
{
    public LogitechMouse() : base() { }
}
internal class Programmer : ConstructBase, IPerson
{
    public IComputer Computer { get; set; }
    public IKeyboard Keyboard { get; set; }
    public IMouse Mouse { get; set; }

    public Programmer(IComputer computer, IKeyboard keyboard, IMouse mouse) : base()
    {
        Computer = computer;
        Keyboard = keyboard;
        Mouse = mouse;
    }

    public void Work()
    {
        Console.WriteLine("The programmer is distributing software...");
    }
}

3.3 依赖注入

IUnityContainer container = new UnityContainer();
container.RegisterType<IComputer, LenovoComputer>(); 
container.RegisterType<IKeyboard, TogarKeyboard>(); 
container.RegisterType<IMouse, LogitechMouse>(); 
container.RegisterType<IPerson, Programmer>(); 
IPerson programmer = container.Resolve<IPerson>(;
programmer.Work();

3.4 输出

LenovoComputer - 被构造了
TogarKeyboard - 被构造了
LogitechMouse - 被构造了
Programmer - 被构造了
The programmer is distributing software...

4 注入方式

4.1 构造函数注入

使用[InjectionConstructor]特性标注依赖注入时使用构造函数,默认使用参数最多的构造函数

[InjectionConstructor]
public Gamer(IComputer computer)
{
    Computer = computer;
}

4.2 属性注入

使用[Dependency]标注需要注入的属性

[Dependency]
public IKeyboard Keyboard { get; set; }

4.3 方法注入

方法注入是为了给方法传递参数,与构造函数注入的代码相似

[InjectionMethod]
public void Inject(IMouse mouse)
{
    this.Mouse = mouse;
}

5 注入顺序

5.1 定义类

internal class Gamer : IPerson
{
    public IComputer Computer { get; set; }
    [Dependency]
    public IKeyboard Keyboard { get; set; }
    public IMouse Mouse { get; set; }

    [InjectionConstructor]
    public Gamer(IComputer computer)
    {
        Computer = computer;
    }

    [InjectionMethod]
    public void Inject(IMouse mouse)
    {
        this.Mouse = mouse;
    }

    public void Work()
    {
        Console.WriteLine("The player is playing...");
    }
}

5.2 调用

IUnityContainer container = new UnityContainer();
container.RegisterType<IPerson, Gamer>(); 
container.RegisterType<IComputer, LenovoComputer>(); 
container.RegisterType<IKeyboard, TogarKeyboard>(); 
container.RegisterType<IMouse, LogitechMouse>(); 
IPerson person = container.Resolve<IPerson>();
person.Work();

5.3 输出

LenovoComputer - 被构造了
TogarKeyboard - 被构造了
LogitechMouse - 被构造了
The player is playing...

5.4 结果

注入顺序:构造函数注入 > 属性注入 > 方法注入

6 接口注册

6.1 重复注册

IUnityContainer container = new UnityContainer();
container.RegisterType<IComputer, LenovoComputer>(); 
container.RegisterType<IKeyboard, TogarKeyboard>(); 
container.RegisterType<IMouse, LogitechMouse>(); 
container.RegisterType<IPerson, Gamer>(); 
container.RegisterType<IPerson, Programmer>(); 
IPerson person = container.Resolve<IPerson>();
person.Work();

输出

LenovoComputer - 被构造了
TogarKeyboard - 被构造了
LogitechMouse - 被构造了
Programmer - 被构造了
The programmer is distributing software...

调换Gamer和Programmer的注册顺序:

IUnityContainer container = new UnityContainer();
container.RegisterType<IComputer, LenovoComputer>(); 
container.RegisterType<IKeyboard, TogarKeyboard>(); 
container.RegisterType<IMouse, LogitechMouse>(); 
container.RegisterType<IPerson, Programmer>(); 
container.RegisterType<IPerson, Gamer>(); 
IPerson person = container.Resolve<IPerson>();
person.Work();

输出

LenovoComputer - 被构造了
Gamer - 被构造了
TogarKeyboard - 被构造了
LogitechMouse - 被构造了
The player is playing...

结论

同一接口多次注册时,后注册的会覆盖前面注册的,若需要实现多重注册,需要指定名称

IUnityContainer container = new UnityContainer();
container.RegisterType<IComputer, LenovoComputer>(); 
container.RegisterType<IKeyboard, TogarKeyboard>(); 
container.RegisterType<IMouse, LogitechMouse>(); 
container.RegisterType<IPerson, Programmer>("Programmer"); 
container.RegisterType<IPerson, Gamer>("Gamer"); 
IPerson programmer = container.Resolve<IPerson>("Programmer");
programmer.Work();
IPerson gamer = container.Resolve<IPerson>("Gamer");
gamer.Work();

输出

LenovoComputer - 被构造了
TogarKeyboard - 被构造了
LogitechMouse - 被构造了
Programmer - 被构造了
The programmer is distributing software...
LenovoComputer - 被构造了
Gamer - 被构造了
TogarKeyboard - 被构造了
LogitechMouse - 被构造了
The player is playing...

6.2 指定参数注册

修改接口

internal interface IMouse
{
    string Type { get; set; }
}

修改实现类

internal class LogitechMouse : ConstructBase, IMouse
{
    public LogitechMouse() : base() { }

    public LogitechMouse(string type) : base()
    {
        Type = type;
    }

    public string Type { get; set; }
}

调用

IUnityContainer container = new UnityContainer();
container.RegisterType<IMouse, LogitechMouse>(new InjectionConstructor("502"));
IMouse mouse = container.Resolve<IMouse>();
Console.WriteLine("Type: " + mouse.Type);

输出

LogitechMouse - 被构造了
Type: 502

多次注册同样覆盖

IUnityContainer container = new UnityContainer();
container.RegisterType<IMouse, LogitechMouse>(new InjectionConstructor("502"));
IMouse mouse_502 = container.Resolve<IMouse>();
Console.WriteLine("Type: " + mouse_502.Type);
container.RegisterType<IMouse, LogitechMouse>(new InjectionConstructor("304"));
IMouse mouse_304 = container.Resolve<IMouse>();
Console.WriteLine("Type: " + mouse_304.Type);

输出

LogitechMouse - 被构造了
Type: 502
LogitechMouse - 被构造了
Type: 304

7 生命周期

生命周期类型:

  • ContainerControlledLifetimeManager:每次调用Resolve(…)方法或依赖机制将实例注入其他类时,Unity都会返回相同的实例。
    • 每个容器生命周期允许将一个已存在或已解决的对象注册为创建或注册它的容器中的作用域单例。换句话说,这个实例在it war注册的容器中是唯一的。子容器或父容器可以为相同的契约注册自己的实例。当ContainerControlledLifetimeManager被释放时,实例也会被释放。
  • ExternallyControlledLifetimeManager:一个LifetimeManager,保存了一个指向它管理的实例的弱引用。
  • HierarchicalLifetimeManager:一个特殊的生命周期管理器,它的工作方式类似于ContainerControlledLifetimeManager,除了在有子容器的情况下,每个子容器都有自己的对象实例,而不是在公共父容器中共享一个。
    • Unity容器允许创建子容器的层次结构。这个生命周期为层次结构的每一层创建局部单例。所以,当你解析一个类型,而这个容器没有该类型的实例时,容器会创建新的实例。类型解析后返回相同的实例。如果创建了一个子容器并要求解析该类型,子容器将创建一个新实例并存储它以供后续解析。下次子容器请求解析类型时,它将返回存储的实例。如果你有多个子节点,每个子节点将解析自己的实例。
  • PerResolveLifetimeManager:这是一个自定义的生命周期管理器,作用类似于TransientLifetimeManager,但它也为默认构建计划提供信号,标记类型,以便在构建对象图中重用实例。
  • PerThreadLifetimeManager:一个LifetimeManager,它会为每个线程创建一个注册类型的新实例。
    • 每个线程生命周期意味着每个线程将创建一个已注册类型的新实例。换句话说,如果第一次在线程上调用Resolve{T}()方法,它将返回一个新对象。每次后续调用Resolve{T}(),或者依赖机制将该类型的实例注入同一线程上的其他类时,容器将返回相同的对象。这个LifetimeManager不会释放它持有的实例。
  • SingletonLifetimeManager:单例生命周期创建全局唯一的单例。任何Unity容器树(父容器和所有子容器)都保证只有一个注册类型的全局单例。
    • 使用单例生命周期注册类型总是将注册放在容器树的根位置,并使该容器的所有子节点都可以全局使用它。无论注册是否发生在子容器的根节点,目标始终是根节点。在任何具有单例生命周期的子节点上重复注册将始终覆盖根注册。当SingletonLifetimeManager被释放时,它持有的实例也会被释放。
  • SynchronizedLifetimeManager:生命周期管理器的基类,需要同步调用GetValue(ILifetimeContainer)。
    • 该类的目的是提供生命周期管理器同步模式的基本实现。SynchronizedLifetimeManager实例的GetValue(ILifetimeContainer)方法会获得一个锁,如果实例还没有初始化一个值,那么这个锁只会在调用SetValue(Object, ILifetimeContainer)方法进行初始化时释放,或者在调用GetValue方法的构建请求失败时释放。
  • TransientLifetimeManager:一个不做任何操作的LifetimeManager实现,从而确保每次都创建新的实例。
    • Transient lifetime是Unity容器的默认生命周期。顾名思义,它持续的时间很短,实际上,根本没有时间。在Unity容器术语中,拥有短暂的生命周期等同于没有生命周期管理器。

默认生命周期

IUnityContainer container = new UnityContainer();
container.RegisterType<IComputer, LenovoComputer>();
IComputer computer0 = container.Resolve<IComputer>();
IComputer computer1 = container.Resolve<IComputer>();
Console.WriteLine("computer0: " + computer0.GetHashCode());
Console.WriteLine("computer1: " + computer1.GetHashCode());
Console.WriteLine($"computer0 == computer1: {computer0 == computer1}");

输出

LenovoComputer - 被构造了
LenovoComputer - 被构造了
computer0: 15368010
computer1: 4094363
computer0 == computer1: False

单例生命周期

IUnityContainer container = new UnityContainer();
container.RegisterType<IComputer, LenovoComputer>(new SingletonLifetimeManager());
IComputer computer0 = container.Resolve<IComputer>();
IComputer computer1 = container.Resolve<IComputer>();
Console.WriteLine("computer0: " + computer0.GetHashCode());
Console.WriteLine("computer1: " + computer1.GetHashCode());
Console.WriteLine($"computer0 == computer1: {computer0 == computer1}");

输出

LenovoComputer - 被构造了
computer0: 15368010
computer1: 15368010
computer0 == computer1: True

每个线程一个生命周期

IUnityContainer container = new UnityContainer();
container.RegisterType<IComputer, LenovoComputer>(new PerThreadLifetimeManager());
IComputer computer0 = container.Resolve<IComputer>();
IComputer computer1 = container.Resolve<IComputer>();
IComputer computer2 = null;
Task.Run(() =>
{
    computer2 = container.Resolve<IComputer>();
}).Wait();
Console.WriteLine("computer0: " + computer0.GetHashCode());
Console.WriteLine("computer1: " + computer1.GetHashCode());
Console.WriteLine("computer2: " + computer2.GetHashCode());
Console.WriteLine($"computer0 == computer1: {computer0 == computer1}");
Console.WriteLine($"computer0 == computer2: {computer0 == computer2}");

输出

LenovoComputer - 被构造了
LenovoComputer - 被构造了
computer0: 15368010
computer1: 15368010
computer2: 4094363
computer0 == computer1: True
computer0 == computer2: False

8 依赖配置

Nuget

19e7fbc5fd41960a979483e3bd12b44b

配置文件

<configuration>
	<configSections>
		<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/>
	</configSections>
	<unity>
		<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configuration"/>
		<containers>
			<container name="Unity_Container">
				<!-- type="接口完全限定名, DLL文件名";mapTo="实现类完全限定名, DLL文件名" -->
				<register type="IoCStudy.Interfaces.IKeyboard, IoCStudy" mapTo="IoCStudy.Entities.TogarKeyboard, IoCStudy"/>
				<register type="IoCStudy.Interfaces.IMouse, IoCStudy" mapTo="IoCStudy.Entities.LogitechMouse, IoCStudy"/>
				<register type="IoCStudy.Interfaces.IComputer, IoCStudy" mapTo="IoCStudy.Entities.LenovoComputer, IoCStudy"/>
				<register type="IoCStudy.Interfaces.IPerson, IoCStudy" mapTo="IoCStudy.Entities.Programmer, IoCStudy" name="Programmer"/>
				<register type="IoCStudy.Interfaces.IPerson, IoCStudy" mapTo="IoCStudy.Entities.Gamer, IoCStudy" name="Gamer"/>
			</container>
		</containers>
	</unity>
</configuration>

调用

ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "DI-Unity.config");
Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
UnityConfigurationSection section = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName);
IUnityContainer container = new UnityContainer();
section.Configure(container, "Unity_Container");
IPerson programmer = container.Resolve<IPerson>("Programmer");
programmer.Work();
IPerson gamer = container.Resolve<IPerson>("Gamer");
gamer.Work();

输出

LenovoComputer - 被构造了
TogarKeyboard - 被构造了
LogitechMouse - 被构造了
Programmer - 被构造了
The programmer is distributing software...
LenovoComputer - 被构造了
Gamer - 被构造了
TogarKeyboard - 被构造了
LogitechMouse - 被构造了
The player is playing...

9 参考资料

IOC容器:Unity_51CTO博客_unity navmesh

IOC容器Unity三种注入总结_unity容器-CSDN博客

IOC容器之Unity与AutoFac_unity autofac-CSDN博客

控制容器的反转和依赖关系注入 模式 (martinfowler.com)

unitycontainer/unity at release/6.0.0 (github.com)

Unity 容器介绍 |Unity 容器 (unitycontainer.org)

标签:容器,container,RegisterType,computer0,构造,Unity,NET,IoC
From: https://www.cnblogs.com/winemonk/p/18534708

相关文章

  • .NET IoC 容器(三)Autofac
    .NETIoC容器(三)Autofac1AutofacAutofac是一个用于.NET应用程序的依赖注入(DependencyInjection,DI)容器。它帮助开发人员管理对象的创建和生命周期,使得依赖项的注入更加灵活和可维护。以下是Autofac的主要功能和特性概述:依赖注入(DependencyInjection)Autofac允......
  • 分布式事物传递 NetMQ测试
    usingNetMQ;usingNetMQ.Sockets;usingSystem;usingSystem.Threading;namespace消息传递库_NetMQ服务端{internalclassProgram{publicstaticvoidMain(){using(varpublisher=newPublisherSocket()){......
  • CommunityToolkit.Mvvm框架
    .NETWPFCommunityToolkit.Mvvm框架1源生成器1.1ObservablePropertyAttribute&RelayCommandAttributeusingCommunityToolkit.Mvvm.ComponentModel;usingCommunityToolkit.Mvvm.Input;usingSystem.Windows;namespaceTestCommunityToolkit._1_Attribute{pub......
  • DevExpress JS & ASP.NET Core v24.1亮点 - 支持DateOnly/TimeOnly类型
    DevExtreme拥有高性能的HTML5/JavaScript小部件集合,使您可以利用现代Web开发堆栈(包括React,Angular,ASP.NETCore,jQuery,Knockout等)构建交互式的Web应用程序。从Angular和Reac,到ASP.NETCore或Vue,DevExtreme包含全面的高性能和响应式UI小部件集合,可在传统Web和下一代移动应用程序中......
  • 【K8S问题系列】Kubernetes Pod节点CrashLoopBackOff 状态【已解决】
    在Kubernetes中,Pod的状态为CrashLoopBackOff表示某个容器在启动后崩溃,Kubernetes尝试重启该容器,但由于持续崩溃,重启的间隔时间逐渐增加。下面将详细介绍CrashLoopBackOff状态的原因、解决方案及相关命令的输出解释。一、CrashLoopBackOff状态的详细介绍描述C......
  • dotnet cli publish 命令行发布脚本 bat shell
    dotnetcli发布脚本publish_remote.bat该脚本实现一键主要发布到远程的共享文件夹中@echooff::定义变量SHARE_PATH:共享文件夹,SOURCE_DIR:代码发布的文件夹,TARGET_DIR:目标文件夹(共享文件夹的映射)setPRO_PATH=.\WebAppsetSHARE_PATH=\\pcmesone\Reportset......
  • chrome浏览器network控制台使用和功能介绍
    chrome浏览器network的控制面板主要分为7大板块1、功能区2、筛选区(功能区漏斗需要开启)3、快照区(功能区需要打开屏幕捕获)4、时间轴区(功能区需要开启overview)5、主显示区6、信息汇总区 一、功能区1、红色圆点代表是否开启network的日志功能,如果灰色的,就代......
  • Acunetix v24.9 发布下载,新增功能概览
    Acunetixv24.9(Linux,Windows)-Web应用程序安全测试Acunetix|WebApplicationSecurityScanner请访问原文链接:https://sysin.org/blog/acunetix/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.org重要提示AcunetixPremium现在使用日历化版本命名。请......
  • .net网页验证码、登录验证码
    来源:https://blog.csdn.net/Yuhang_Zhou/article/details/140614304验证码辅助类usingSystem.Drawing;usingSystem.Drawing.Imaging;namespaceXCGApp{///<summary>///验证码辅助类///</summary>publicclassValidateCodeUtil{/......
  • dotnet core微服务框架Jimu ~ 会员注册微服务
     提供会员注册服务,用户必须注册成会员才能享受应用提供的服务,如浏览和发布新闻,但有些服务又需要指定角色的会员才能操作,如所有会员都可以浏览新闻,只有管理员(admin)角色的会员才可以发布新闻。有2个公开的api:CheckName:判断用户名是否可用;Register:根据用户名注册......