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 .大致浏览了下感觉 ...
随机推荐
- SQL Server出现错误: 4014
SQL Server出现错误: 4014 线下的测试机器老是报错,从errorlog里看到大量的4014错误 A fatal error occurred , output error: ). 错误: ...
- ABP理论学习之MVC视图
返回总目录 本篇目录 介绍 AbpWebViewPage基类 介绍 ABP通过Abp.Web.Mvc Nuget包集成了MVC视图.因此你可以像常规那样创建MVC视图. AbpWebViewPage基 ...
- Mina入门实例(一)
mina现在用的很多了,之前也有用到,但是毕竟不熟悉,于是查了一些资料,做了一些总结.看代码是最直观的,比什么长篇大论都要好.不过其中重要的理论,也要理解下. 首先是环境,程序运行需要几个包,这里用m ...
- Python黑帽编程1.2 基于VS Code构建Python开发环境
Python黑帽编程1.2 基于VS Code构建Python开发环境 0.1 本系列教程说明 本系列教程,采用的大纲母本为<Understanding Network Hacks Atta ...
- 自己动手写一个简单的MVC框架(第一版)
一.MVC概念回顾 路由(Route).控制器(Controller).行为(Action).模型(Model).视图(View) 用一句简单地话来描述以上关键点: 路由(Route)就相当于一个公司 ...
- 一起学习jQuery2.0.3源码—1.开篇
write less,do more jQuery告诉我们:牛逼的代码不仅精简而且高效! 2006年1月由美国人John Resig在纽约的barcamp发布了jQuery,吸引了来自世界各地众多Ja ...
- 当程序以Windows Services形式启动时当前路径不对
当程序以Windows Services形式启动时当前路径不对 @(操作系统)[博客|dotNet] 很多时候我们需要将我们的程序写成利用Windows服务的形式来让它能够自启动.今天遇到一个问题,当 ...
- 微冷的雨之Java中的多线程初理解(一)
在讲解多线程前,我们必须理解什么是多线程?而且很多人都会将进程和线程做对比. 进程和线程 进程:进程是操作系统结构的基础,是一次程序的执行,是一个程序及其数据在处理机上顺序执行时所发生的活动,是程序在 ...
- GitHub iOS-Top 100 简介
GitHub排名前100的iOS第三方汇总简介,方便开发者选择适合的第三方框架. 项目名称 项目信息 1. AFNetworking 作者是 NSHipster 的博主, iOS 开发界的大神级人物, ...
- java基础-多线程执行
package Thanqi; public class TestApple implements Runnable{ //苹果的数量 private int count = 5; //拿苹果 //s ...