IoC原理-使用反射/Emit来实现一个最简单的IoC容器
从Unity到Spring.Net,到Ninject,几年来陆陆续续用过几个IoC框架。虽然会用,但也没有一直仔细的研究过IoC实现的过程。最近花了点时间,下了Ninject的源码,研究了一番,颇有收获。下面我要实现一个最最简单的IoC容器,以让跟我一样的小菜能更好的理解IoC框架的到底为我们做了什么。
什么是IoC
IoC是英文Inversion of Control的缩写。我们一般叫它“控制反转”。IoC技术是用来解决面向对象设计一大原则依赖倒置而出现的技术。可以更好的实现面向接口编程,来使各个组件之间解耦。
IoC的实现原理
.NET IoC容器的一般就是两种,一是反射,二是使用Emit来直接写IL。
废话不多了,想要了解跟多的IoC的知识请Google。
关于实现
先上一张类图

1.定义IIoCConfig接口
public interface IIoCConfig
{
void AddConfig<TInterface,TType>(); Dictionary<Type, Type> ConfigDictionary { get; }
}
2.定义IoCConfig实现
public class IoCConfig:IIoCConfig
{
/// <summary>
/// 存放配置的字典对象,KEY是接口类型,VALUE是实现接口的类型
/// </summary>
private Dictionary<Type, Type> _configDictionary=new Dictionary<Type, Type>(); /// <summary>
/// 添加配置
/// </summary>
/// <typeparam name="TInterface">接口</typeparam>
/// <typeparam name="TType">实现接口的类型</typeparam>
public void AddConfig<TInterface, TType>()
{
//判断TType是否实现TInterface
if (typeof(TInterface).IsAssignableFrom(typeof(TType)))
{
_configDictionary.Add(typeof(TInterface), typeof(TType));
}
else
{
throw new Exception("类型未实现接口");
}
} public Dictionary<Type, Type> ConfigDictionary
{
get
{
return _configDictionary;
}
}
}
使用一个字典来保存Interface跟Class的对应关系。这里是仿造Ninject的配置方式,使用代码来配置。这种配置方式有个好处就是不会写错,因为有IDE来给你检查拼写错误。不要小看这个好处,当你有上百个注入对象的时候,使用Unity的XML来配置对应关系的时候很容易就会发生拼写错误。这种错误往往还很难发现。
当然这里要实现一个按照XML配置文件来设置对应关系的类也很容易,这里就不实现了。
3.定义IIoCContainer容器接口
public interface IIoCContainer
{
/// <summary>
/// 根据接口返回对应的实例
/// </summary>
/// <typeparam name="TInterface"></typeparam>
/// <returns></returns>
TInterface Get<TInterface>();
}
4.使用反射实现IoC容器
public class ReflectionContainer:IIoCContainer
{
/// <summary>
/// 配置实例
/// </summary>
private IIoCConfig _config; /// <summary>
/// 构造函数
/// </summary>
/// <param name="config">ioc配置</param>
public ReflectionContainer(IIoCConfig config)
{
_config = config;
} /// <summary>
/// 根据接口获取实例对象
/// </summary>
/// <typeparam name="TInterface">接口</typeparam>
/// <returns></returns>
public TInterface Get<TInterface>()
{
Type type;
var can = _config.ConfigDictionary.TryGetValue(typeof(TInterface), out type);
if (can)
{
//反射实例化对象
return (TInterface)Activator.CreateInstance(type);
}
else
{
throw new Exception("未找到对应的类型");
}
}
}
反射这个代码太简单了,大家都会用。
5.使用Emit实现IoC容器
public class EmitContainer:IIoCContainer
{
/// <summary>
/// 配置实例
/// </summary>
private IIoCConfig _config; public EmitContainer(IIoCConfig config)
{
_config = config;
} /// <summary>
/// 获取实例
/// </summary>
/// <typeparam name="TInterface">接口</typeparam>
/// <returns></returns>
public TInterface Get<TInterface>()
{
Type type;
var can = _config.ConfigDictionary.TryGetValue(typeof(TInterface), out type);
if (can)
{
BindingFlags defaultFlags = BindingFlags.Public | BindingFlags.Instance;
var constructors = type.GetConstructors(defaultFlags);//获取默认构造函数
var t = (TInterface)this.CreateInstanceByEmit(constructors[0]);
return t;
}
else
{
throw new Exception("未找到对应的类型");
}
} /// <summary>
/// 实例化对象 用EMIT
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="constructor"></param>
/// <returns></returns>
private Object CreateInstanceByEmit(ConstructorInfo constructor)
{
//动态方法
var dynamicMethod = new DynamicMethod(Guid.NewGuid().ToString("N"), typeof(Object), new[] { typeof(object[]) }, true);
//方法IL
ILGenerator il = dynamicMethod.GetILGenerator();
//实例化命令
il.Emit(OpCodes.Newobj, constructor);
//如果是值类型装箱
if (constructor.ReflectedType.IsValueType)
il.Emit(OpCodes.Box, constructor.ReflectedType);
//返回
il.Emit(OpCodes.Ret);
//用FUNC去关联方法
var func = (Func<Object>)dynamicMethod.CreateDelegate(typeof(Func<Object>));
//执行方法
return func.Invoke();
}
}
Emit的实现是抄自Ninject的实现方式。这里其实就是在手动书写IL。一个简单的书写IL的办法就是先用C#写好代码,然后用Reflector等反编译工具查看生成的IL,然后改成Emit代码。
6.实现IoCContainerManager
public class IoCContainerManager
{
/// <summary>
/// 容器
/// </summary>
private static IIoCContainer _container; /// <summary>
/// 获取IOC容器
/// </summary>
/// <param name="config">ioc配置</param>
/// <returns></returns>
public static IIoCContainer GetIoCContainer(IIoCConfig config)
{ if (_container==null)
{
//反射方式
_container = new ReflectionContainer(config);
//EMIT方式
// _container=new EmitContainer(config);
}
return _container; }
}
代码太简单,不多说了。
7.使用
public interface ITest
{
void DoWork();
} public class Test:ITest
{
public void DoWork()
{
Console.WriteLine("do work!");
}
} class Program
{
static void Main(string[] args)
{
IIoCConfig config = new IoCConfig();
config.AddConfig<ITest, Test>();//添加配置
//获取容器
IIoCContainer container = IoCContainerManager.GetIoCContainer(config);
//根据ITest接口去获取对应的实例
ITest test = container.Get<ITest>(); test.DoWork(); Console.Read();
}
}
输出:

这里手动使用IoC容器去获取对应的实例对象,我们也可以配合特性来使代码更加简单。这里就不实现了。
8.总结
通过这么短短的几行代码。我们实现了一个最最简单的IoC容器。它可以实现构造函数注入(默认无参)。但是这就已经揭示了IoC框架最本质的东西:反射或者EMIT来实例化对象。然后我们可以加上缓存,或者一些策略来控制对象的生命周期,比如是否是单例对象还是每次都生成一个新的对象。
IoC原理-使用反射/Emit来实现一个最简单的IoC容器的更多相关文章
- 手写一个最简单的IOC容器,从而了解spring的核心原理
从事开发工作多年,spring源码没有特意去看过.但是相关技术原理倒是背了不少,毕竟面试的那关还是得过啊! 正所谓面试造火箭,工作拧螺丝.下面实现一个最简单的ioc容器,供大家参考. 1.最终结果 2 ...
- 【spring】-- 手写一个最简单的IOC框架
1.什么是springIOC IOC就是把每一个bean(实体类)与bean(实体了)之间的关系交给第三方容器进行管理. 如果我们手写一个最最简单的IOC,最终效果是怎样呢? xml配置: <b ...
- 【最简单IOC容器实现】实现一个最简单的IOC容器
前面DebugLZQ的两篇博文: 浅谈IOC--说清楚IOC是什么 IoC Container Benchmark - Performance comparison 在浅谈IOC--说清楚IOC是什么 ...
- 自己动手实现一个简单的 IOC容器
控制反转,即Inversion of Control(IoC),是面向对象中的一种设计原则,可以用有效降低架构代码的耦合度,从对象调用者角度又叫做依赖注入,即Dependency Injection( ...
- 深入理解Spring--动手实现一个简单的SpringIOC容器
接触Spring快半年了,前段时间刚用Spring4+S2H4做完了自己的毕设,但是很明显感觉对Spring尤其是IOC容器的实现原理理解的不到位,说白了,就是仅仅停留在会用的阶段,有一颗想读源码的心 ...
- Java反射机制及IoC原理
一. 反射机制概念 主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义.在java中,只要给定类的名字, 那么就可以通 ...
- spring ioc原理(看完后大家可以自己写一个spring)
控制反转/依赖注入 最近,买了本Spring入门书:spring In Action .大致浏览了下感觉还不错.就是入门了点.Manning的书还是不错的,我虽然不像哪些只看Manning书的人那样专 ...
- (转)spring ioc原理(看完后大家可以自己写一个spring)
最近,买了本Spring入门书:spring In Action .大致浏览了下感觉还不错.就是入门了点.Manning的书还是不错的,我虽然不像哪些只看Manning书的人那样专注于Manning, ...
- (转)spring ioc原理(看完后大家可以自己写一个spring)
原文地址:https://blog.csdn.net/it_man/article/details/4402245 最近,买了本Spring入门书:spring In Action .大致浏览了下感觉 ...
随机推荐
- 译文---C#堆VS栈(Part One)
前言 本文主要是讲解C#语言在内存中堆.栈的使用情况,使读者能更好的理解值类型.引用类型以及线程栈.托管堆. 首先感谢原文作者:Matthew Cochran 为我们带来了一篇非常好的文章,并配以大量 ...
- 《CLR.via.C#第三版》第二部分第12章节 泛型 读书笔记(六)
终于讲到泛型了.当初看到这个书名,最想看的就是作者对泛型,委托,反射这些概念的理解.很多人对泛型的理解停留在泛型集合上,刚开始我也是,随着项目越做越多,对待泛型的认识也越来越深刻. 泛型的概念:泛型是 ...
- SQL语句到底是怎么执行的
写在前面的话:有时不理解SQL语句各个部分执行顺序,导致理解上出现偏差,或者是书写SQL语句时随心所欲,所以有必要了解一下sql语句的执行顺序.可以有时间自己写一个简单的数据库,理解会更加深入.下面就 ...
- 介绍两个Ubuntu上的桌面小工具
经常使用Windows10,Sticky Notes和壁纸自动切换功能挺好用的.我经常会使用Sticky Notes来记录一些信息,内容是实时保存的,而且启动的时候会自动显示在桌面上.其实Ubuntu ...
- Objective-C 工厂模式(下) -- 抽象工厂模式
相比简单工厂模式, 只有一个工厂 能生产的手机也是固定的 抽象工厂模式类似于有很多家工厂, 当用户要买什么手机就创建对应的工厂去生产 比如用户要买iPhone就创建一个Apple工厂来生产手机, 要买 ...
- 前端编码规范之JavaScript
上次浅谈了下关于CSS的编码规范,大部分童鞋持赞同意见,仍存在一些童鞋不太理解这些规范的意义. 如果是个人或者小作坊开发,其实这些所谓的编码规范也没啥意思,因为大家写好的代码直接就给扔到网上去了,很少 ...
- C#学习系列-文章导航
C#学习系列-.NET体系结构 C#学习系列-类与结构的区别 C#学习系列-String与string的区别 C#学习系列-抽象方法与虚拟方法的区别 C#学习系列-out与ref的区别 C#学习系列- ...
- jQuery 2.0.3 源码分析 回溯魔法 end()和pushStack()
了解了jQuery对DOM进行遍历背后的工作机制,可以在编写代码时有意识地避免一些不必要的重复操作,从而提升代码的性能 从这章开始慢慢插入jQuery内部一系列工具方法的实现 关于jQuery对象的包 ...
- JS preventDefault ,stopPropagation ,return false
所谓的事件有两种:监听事件和浏览器对特殊标签元素的默认行为事件.监听事件:在节点上被监听的事件操作,如 select节点的change事件,a节点的click事件.浏览器的默认事件:特定页面元素上带的 ...
- 深入理解脚本化CSS系列第三篇——脚本化CSS类
前面的话 在实际工作中,我们使用javascript操作CSS样式时,如果要改变大量样式,会使用脚本化CSS类的技术,本文将详细介绍脚本化CSS类 style 我们在改变元素的少部分样式时,一般会直接 ...