前言

  最近在调试EasyNetQ代码的时候发现里面有一段代码,就是IoC容器的简单实现,跟着他的代码敲了一遍,发现了奇妙之处。当然也是因为我才疏学浅导致孤陋寡闻了。他的思路就是通过动态调用构造函数生成对象,然后将对象保存,调用的时候进行单例调用,而且,代码中不会存在 new 字眼。所有实例对象的创建和映射都在容器中实现。当然,还是用第三方的容器比较稳妥,本文中只是很简单的一个示范。具体理解的是否正确,我也不敢说,只不过,能达到一些预期的效果,功能不够强大。

解析

  首先,我们先添加几个接口。IServiceProvider,IServiceRegister,IServiceContainer (继承自前两个)

 /// <summary>
/// 服务提供接口
/// </summary>
public interface IServiceProvider
{
/// <summary>
/// 获取某个类型的服务实例
/// </summary>
/// <typeparam name="TService"> TService 接口 </typeparam>
/// <returns>返回TService类型实例</returns>
TService Resolve<TService>() where TService : class;
}
 /// <summary>
/// 服务注册接口
/// </summary>
public interface IServiceRegister
{
/// <summary>
/// 注册一个服务工厂
/// </summary>
/// <typeparam name="TService">工厂类</typeparam>
/// <param name="serviceCreator">创建一个新的接口实例</param>
/// <returns>返回当前注册工厂,以便调用其他的register方法</returns>
IServiceRegister Register<TService>(Func<IServiceProvider, TService> serviceCreator) where TService : class; /// <summary>
/// 注册服务类型和服务实例
/// </summary>
/// <typeparam name="TService">接口实例</typeparam>
/// <typeparam name="TImplementation">类型继承自TService</typeparam>
/// <returns>返回当前注册工厂,以便调用其他的register方法</returns>
IServiceRegister Register<TService, TImplementation>()
where TService : class
//TImplementation 继承自 TService
where TImplementation : class, TService;
}
 /// <summary>
/// 对外接口Container,继承自IServiceProvider,IServiceRegister
/// </summary>
public interface IServiceContainer : IServiceProvider, IServiceRegister { }

  当然,最主要的还是 IServiceContainer 的实现,代码中有简单的注释,所以我就直接贴代码了。(看不懂的仔细认真一些就好)

  /// <summary>
/// 服务提供类的实现,类似服务工厂
/// </summary>
public class ServiceProvider : IServiceContainer
{
/// <summary>
/// 锁
/// </summary>
private readonly object syncLock = new object();
/// <summary>
/// 存储单例工厂
/// </summary>
private readonly ConcurrentDictionary<Type, object> factories = new ConcurrentDictionary<Type, object>();
/// <summary>
/// 存储注册类型
/// </summary>
private readonly ConcurrentDictionary<Type, Type> registrations = new ConcurrentDictionary<Type, Type>();
/// <summary>
/// 存储实例
/// </summary>
private readonly ConcurrentDictionary<Type, object> instances = new ConcurrentDictionary<Type, object>(); #region 私有方法
/// <summary>
/// 判断服务是否已经被注册过
/// </summary>
/// <param name="serviceType"></param>
/// <returns></returns>
private bool ServiceIsRegistered(Type serviceType)
{
//判断是否在工厂或者注册库内已经注册过该类型
return factories.ContainsKey(serviceType) || registrations.ContainsKey(serviceType);
}
#endregion /// <summary>
/// 注册工厂
/// </summary>
/// <typeparam name="TService"></typeparam>
/// <param name="serviceCreator"></param>
/// <returns></returns>
public IServiceRegister Register<TService>(Func<IServiceProvider, TService> serviceCreator) where TService : class
{
lock (syncLock) {
var serviceType = typeof(TService);
if (ServiceIsRegistered(serviceType)) { return this; } //将服务添加到存储器中
factories.TryAdd(serviceType, serviceCreator);
return this;
}
} /// <summary>
/// 注册实例类
/// </summary>
/// <typeparam name="TService">IService 必须实现一个构造器</typeparam>
/// <typeparam name="TImplementation"></typeparam>
/// <returns></returns>
public IServiceRegister Register<TService, TImplementation>() where TService : class where TImplementation : class, TService
{
lock (syncLock)
{
var serviceType = typeof(TService);
var implType = typeof(TImplementation); if (ServiceIsRegistered(serviceType)) { return this; } //如果注册的类不是继承自TService接口,抛出异常
if (!serviceType.IsAssignableFrom(implType))
{
throw new Exception(string.Format("类型 {0} 不继承接口 {1}", implType.Name, serviceType.Name));
}
//获取构造方法,必须只有一个构造方法(why?)
var constructors = implType.GetConstructors();
if (constructors.Length != )
{
throw new Exception(string.Format("服务实例必须有且只有一个构造方法.当前实例 {0} 拥有 {1} 个", implType.Name, constructors.Length.ToString()));
}
//添加
registrations.TryAdd(serviceType, implType);
return this;
}
} /// <summary>
/// 返回一个实例
/// </summary>
/// <typeparam name="TService"></typeparam>
/// <returns></returns>
public virtual TService Resolve<TService>() where TService : class
{
var serviceType = typeof(TService);
object service;
//如果实例存储器中已经存在该实例,直接返回
if (instances.TryGetValue(serviceType, out service))
return (TService)service;
lock (syncLock)
{
//加锁,再次判断
if (instances.TryGetValue(serviceType, out service))
{
return (TService)service;
} //如果注册器中存在该类型,创建该实例,并加入到实例存储器中
if (registrations.ContainsKey(serviceType))
{
var implementationType = registrations[serviceType];
service = CreateServiceInstance(implementationType);
instances.TryAdd(serviceType, service);
}
else if (factories.ContainsKey(serviceType))
{
service = ((Func<IServiceProvider, TService>)factories[serviceType])(this);
instances.TryAdd(serviceType, service);
}
else
{
throw new Exception(string.Format("服务类型 {0} 未注册", serviceType.Name));
}
return (TService)service;
}
} private object Resolve(Type serviceType)
{
return GetType()
.GetMethod("Resolve", new Type[])
.MakeGenericMethod(serviceType)
.Invoke(this, new object[]);
} private object CreateServiceInstance(Type implementationType)
{
//获取构造器
var constructors = implementationType.GetConstructors();
//遍历构造器中的参数类型,获取参数
var parameters = constructors[]
.GetParameters()
.Select(parameterInfo => Resolve(parameterInfo.ParameterType))
.ToArray(); //创建实例方法
return constructors[].Invoke(parameters);
}
}

调用方式

写个测试:IAnimal 接口中有Shout方法。在创建两个类继承自IAnimal,Dog,Cat。 IPeople中有个Pet方法,在创建两个类,Man,Women 继承自IPeople。 其中IPeople中初始化要传入IAnimal的实例,来确定养的宠物是猫还是狗.

代码如下:

    public interface IAnimal
{
void Shout();
} public class Dog : IAnimal
{
public void Shout()
{
Console.WriteLine("汪汪汪");
}
} public class Cat : IAnimal
{
public void Shout()
{
Console.WriteLine("喵喵喵");
}
}
    public interface IPeople
{
void Pet();
} public class Man : IPeople
{
private IAnimal _animal; public Man(IAnimal animal)
{
_animal = animal;
}
public void Pet()
{
Console.WriteLine("[男]我有一个宠物,它在:");
_animal.Shout();
}
} public class Woman : IPeople
{
private IAnimal _animal; public Woman(IAnimal animal)
{
_animal = animal;
}
public void Pet()
{
Console.WriteLine("[女]我有一个宠物,它在:");
_animal.Shout();
}
}

测试代码

  static void Main(string[] args)
{
Func<IServiceContainer> getContainer = () => new ServiceProvider();
IServiceContainer container = getContainer(); //注册
container.Register(_ => container)
//注册Dog实例
.Register<IAnimal, Dog>()
//注册Woman实例
.Register<IPeople, Woman>(); //得到people
var people = container.Resolve<IPeople>();
//调用pet方法
people.Pet(); Console.Read();
}

执行结果:

同样,当我们把 Dog换成Cat

我们把animal的注册去掉,由于people依赖于animal,所以会报错误。

总结

  这是一个简单的IoC容器的实现,相信真正的要复杂的很多,不过看EasyNetQ就直接这么用的,我觉得如果需求不高的话,也可以用这种方法。而且,代码看起来清洁了许多,不用自己new对象,而且,内部依赖也可以轻松解决。我该去看看真正的容器是怎么实现的了,byebye

几句代码简单实现IoC容器的更多相关文章

  1. IoC原理-使用反射/Emit来实现一个最简单的IoC容器

    从Unity到Spring.Net,到Ninject,几年来陆陆续续用过几个IoC框架.虽然会用,但也没有一直仔细的研究过IoC实现的过程.最近花了点时间,下了Ninject的源码,研究了一番,颇有收 ...

  2. 【最简单IOC容器实现】实现一个最简单的IOC容器

    前面DebugLZQ的两篇博文: 浅谈IOC--说清楚IOC是什么 IoC Container Benchmark - Performance comparison 在浅谈IOC--说清楚IOC是什么 ...

  3. 比Spring简单的IoC容器

    比Spring简单的IoC容器 Spring 虽然比起EJB轻量了许多,但是因为它需要兼容许多不同的类库,导致现在Spring还是相当的庞大的,动不动就上40MB的jar包, 而且想要理解Spring ...

  4. 手写一个最简单的IOC容器,从而了解spring的核心原理

    从事开发工作多年,spring源码没有特意去看过.但是相关技术原理倒是背了不少,毕竟面试的那关还是得过啊! 正所谓面试造火箭,工作拧螺丝.下面实现一个最简单的ioc容器,供大家参考. 1.最终结果 2 ...

  5. .NET实现一个简单的IOC容器

    目录 1.主要细节 2.具体示例 参考及示例代码下载 shanzm-2020年3月17日 20:06:01 1.主要细节 使用反射程序集的方式获取对象的类型 通过反射的方式获取指定类型的的所有公共属性 ...

  6. 自己动手实现一个简单的 IOC容器

    控制反转,即Inversion of Control(IoC),是面向对象中的一种设计原则,可以用有效降低架构代码的耦合度,从对象调用者角度又叫做依赖注入,即Dependency Injection( ...

  7. IL实现简单的IOC容器

    既然了解了IL的接口和动态类之间的知识,何不使用进来项目实验一下呢?而第一反应就是想到了平时经常说的IOC容器,在园子里搜索了一下也有这类型的文章http://www.cnblogs.com/kkll ...

  8. 最简单的ioc容器代码(低仿Spring )

    Spring 的一大核心就是IOC,控制反转(依赖注入). 对象交由容器去控制,降低耦合性. Spring 的ioc实现原理其实很简单,容器启动后读取并解析配置文件,根据配置文件中<bean&g ...

  9. 简单模拟IOC容器:返回对象并能抛出异常

    本次要求:已知com.zzj.vo包下分别有Tiger.lion.Elephant三个Java源文件,请据此实现以下功能:①.自定义一个名为Component的注解,要求该注解只能用于类且代码运行时该 ...

随机推荐

  1. mysql8.0 安装 修改密码 允许远程连接

    转自:https://www.cnblogs.com/xyabk/p/8967990.html mysql从5.7一下子跳跃到了8.0,其中的改变还是很大,有点这里就不说了,小伙伴们自己去百度了解一下 ...

  2. redis(6)lua脚本

    一.lua脚本 lua是一种轻量小巧的脚本语言,用标准的C语言编写并以源代码形式开放,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能. lua的详细内容你可以参考lua官方网站 ...

  3. SZU4

    #include <iostream> #include <string> #include <cstring> #include <cstdlib> ...

  4. 使用PowerShell批量解除锁定下载的文件

    使用PowerShell批量解除锁定下载的文件 3.在需要解锁的文件所在的文件夹中空白处,按住Shift然后单击右键,在弹出的右键菜单中,选择“在此处打开PowerShell窗口”, 输入Get-Ch ...

  5. spring的aop 基于schema

    AOP为Aspect Oriented Programming的缩写,意为:面向切面编程 一 前期工作 1.新建一个java项目,我是使用的maven,所以我新建了一个简单的maven项目,因为mav ...

  6. Maven打包时,不包含jar包

    在给Maven项目打war包时,如果不想把依赖中的jar包也包含进去,可以在plugins中加入 <span style="white-space:pre"> < ...

  7. LeetCode Palidrome Number

    class Solution { public: bool isPalindrome(int x) { ) return false; ; int t = x; ; ) { pow *= ; cnt+ ...

  8. [转]Shared——Javascript中的call详解

    call( ) 一.call的使用 call 方法第一个参数是作为函数上下文的对象,第二个参数是一个参数列表. var obj = { name: 'J' } function func(p1, p2 ...

  9. python apscheduler的使用

    from apscheduler.schedulers.blocking import BlockingSchedulerfrom datetime import datetime def my_jo ...

  10. csharp: DataTable export to excel,word,csv etc

    http://code.msdn.microsoft.com/office/Export-GridView-to-07c9f836 https://exporter.codeplex.com/ htt ...