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 .大致浏览了下感觉 ...
随机推荐
- xcode gdb/lldb调试命令
命令 解释 break NUM 在指定的行上设置断点. bt 显示所有的调用栈帧.该 ...
- 我的第一段jQuery代码
说起 jQuery,很多人可能觉得,不算什么,就是个js类库.而,对于我,下面这几行代码,是一个新的开始. 多年来,我一直在使用MooTools ,他面向对象,写起来结构清晰分明,都是在原生js的基础 ...
- 使用vbs脚本进行批量编码转换
使用vbs脚本进行批量编码转换 最近需要使用SourceInsight查看分析在Linux系统下开发的项目代码,我们知道Linux系统中文本文件默认编码格式是UTF-8,而Windows中文系统中的默 ...
- 入手Invicta 8926 OB潜水自动机械腕表
前个月前就想入手一款手表了,之前在关注和学习.询问他人选哪样的表好,前些天还在看精工Seiko机械表系列,今凌晨有朋友给我推荐这款Invicta 8926系列手表,我一看便喜欢了. 在网上也是搜索了很 ...
- 【原】Python用例:将指定文件或目录打包成zip文件
#This Demo is used to compress files to .zip file #Base on Windows import os import time #The files ...
- VVDocumenter 使用
函数说明文档插件下载链接 https://github.com/onevcat/VVDocumenter-Xcode 下载后,直接运行项目 升级Xcode后可能不能再次使用,需要做一些处理从Xcode ...
- 解决OracleConnection ORA-1017 和 HRESULT:0x8007000B 错误
试图加载格式不正确的程序. (异常来自HRESULT:0x8007000B) 解决方案: IIS下 winform下: ORA-1017 错误
- Entity Framework Core 1.1 Preview 1 简介
实体框架核心(EF Core)是Entity Framework的一个轻量级,可扩展和跨平台版本. 10月25日,Entity Framework Core 1.1 Preview 1发布了. 升级到 ...
- DPI深度包检测
最近在读网络协议方面的论文,接触到DPI技术.博主是个小白,这里写些查到的笔记. 原文出处因为比较多,杂乱.百度文库和许多地方都有,就不贴链接了. 1. DPI 全称为"Deep Packe ...
- Design7:数据删除设计
在设计一个新系统的Table Schema的时候,不仅需要满足业务逻辑的复杂需求,而且需要考虑如何设计schema才能更快的更新和查询数据,减少维护成本. 模拟一个场景,有如下Table Schema ...