从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容器的更多相关文章

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

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

  2. 【spring】-- 手写一个最简单的IOC框架

    1.什么是springIOC IOC就是把每一个bean(实体类)与bean(实体了)之间的关系交给第三方容器进行管理. 如果我们手写一个最最简单的IOC,最终效果是怎样呢? xml配置: <b ...

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

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

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

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

  5. 深入理解Spring--动手实现一个简单的SpringIOC容器

    接触Spring快半年了,前段时间刚用Spring4+S2H4做完了自己的毕设,但是很明显感觉对Spring尤其是IOC容器的实现原理理解的不到位,说白了,就是仅仅停留在会用的阶段,有一颗想读源码的心 ...

  6. Java反射机制及IoC原理

    一. 反射机制概念 主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义.在java中,只要给定类的名字, 那么就可以通 ...

  7. spring ioc原理(看完后大家可以自己写一个spring)

    控制反转/依赖注入 最近,买了本Spring入门书:spring In Action .大致浏览了下感觉还不错.就是入门了点.Manning的书还是不错的,我虽然不像哪些只看Manning书的人那样专 ...

  8. (转)spring ioc原理(看完后大家可以自己写一个spring)

    最近,买了本Spring入门书:spring In Action .大致浏览了下感觉还不错.就是入门了点.Manning的书还是不错的,我虽然不像哪些只看Manning书的人那样专注于Manning, ...

  9. (转)spring ioc原理(看完后大家可以自己写一个spring)

    原文地址:https://blog.csdn.net/it_man/article/details/4402245 最近,买了本Spring入门书:spring In Action .大致浏览了下感觉 ...

随机推荐

  1. shell简单用法笔记(一)

    一.linux中主要用的bash shell:查看linux系统中支持的shell种类可用: vim /etc/shell 执行shel脚步的方式: 1.赋予脚步可执行权限,使用相对或绝对路径调用该脚 ...

  2. Redis系列(五)-Opserver的监控

    阅读目录: 基本介绍 使用配置 部署实例 面板属性 基本介绍 Opserver是Stack Exchange的一个开源监控系统,基于Net.MVC开发,所以Net程序员可以轻松基于它二次开发.它主要监 ...

  3. MySQL 存储过程和函数

    概述 一提到存储过程可能就会引出另一个话题就是存储过程的优缺点,这里也不做讨论,一般别人问我我就这样回答你觉得它好你就用它.因为mysql中存储过程和函数的语法非常接近所以就放在一起,主要区别就是函数 ...

  4. python实现grep

    import sys import os import re def usage(): print "[Usage]: python grep.py filename grepString. ...

  5. 分享自己写的JS版日期格式化和解析工具类,绝对好用!

    前言 本来想模仿Java里面的SimpleDateFormat()对象的,但是感觉这样用起来不方便,所以还是直接写成单独的方法算了. 原文链接 日期格式化 使用说明 formatDate(date, ...

  6. Dash文档制作教程

    前言 什么是Dash 面向程序员的文档库(Mac) 代码片段管理工具 这是强烈推荐给每天在各种API文档中摸爬滚打的程序员们的神器. 为什么要自己制作文档 官方的源中没有相关文档 文档在离线下体验更好 ...

  7. js浏览器对象模型-BOM

    bom browse object model 浏览器对象模型. 也就是window对象下面的东西. location 对象 window.location.href 表示打开窗口的路径. windo ...

  8. ASP.NET Core 1.1 静态文件、路由、自定义中间件、身份验证简介

    概述 之前写过一篇关于<ASP.NET Core 1.0 静态文件.路由.自定义中间件.身份验证简介>的文章,主要介绍了ASP.NET Core中StaticFile.Middleware ...

  9. PHP的性能演进(从PHP5.0到PHP7.1的性能全评测)

    本文是最初是来自国外的这篇:PHP Performance Evolution 2016, 感谢高可用架构公众号翻译成了中文版, 此处是转载的高可用架构翻译后的文章从PHP 5到PHP 7性能全评测( ...

  10. HighCharts从数据库中读取数据

    1.index.js router.get('/', function(req, res, next) { res.render('index', { title: 'Express' }); }); ...