从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. ASP.NET MVC 和 Ruby 相结合的Web框架Oak

    在http://www.asp.net/mvc/open-source 上有个项目Oak: Frictionless development for ASP.NET MVC single page w ...

  2. 一劳永逸:域名支持通配符,ASP.NET Core中配置CORS更轻松

    ASP.NET Core 内置了对 CORS 的支持,使用很简单,只需先在 Startup 的 ConfigureServices() 中添加 CORS 策略: public void Configu ...

  3. Google分布式构建软件之四:分发构建结果

    注:本文英文原文在google开发者工具组的博客上[需要FQ],以下是我的翻译,欢迎转载,但请尊重作者版权,注名原文地址. 之前的文章,介绍了Google在分布式构建软件过程中,如何把构建过程分发到许 ...

  4. 将word文档A表格中的内容拷贝到word文档B表格中

    Function IsFileExists(ByVal strFileName As String) As Boolean ) <> Empty Then IsFileExists = T ...

  5. Eclipse 调试技巧

    条件断点 顾名思义,是指当发生某种情况或者触发某种条件的情况下命中断点.常用的情形就是for循环中某个变量为xx值的时候命中断点类似的. 做法1:在debug视图中,BreakPoint View将所 ...

  6. JavaScript css-dom

    HTML负责结构层,网页的结构层由HTML或者XHTML之类的标记语言负责构建 CSS负责表示层,描述页面内容应该如何呈现. JavaScript负责行为层,负责内容应该如何响应事件这一问题. 能利用 ...

  7. iOS开发-应用崩溃日志揭秘(一)

    作为一名应用开发者,你是否有过如下经历? 为确保你的应用正确无误,在将其提交到应用商店之前,你必定进行了大量的测试工作.它在你的设备上也运行得很好,但是,上了应用商店后,还是有用户抱怨会闪退 ! 如果 ...

  8. KnockoutJS 3.X API 第七章 其他技术(1) 加载和保存JSON数据

    Knockout允许您实现复杂的客户端交互性,但几乎所有Web应用程序还需要与服务器交换数据,或至少将本地存储的数据序列化. 最方便的交换或存储数据的方式是JSON格式 - 大多数Ajax应用程序今天 ...

  9. js修改后没反应-看看是不是取的缓存

  10. SQL Server游标

    什么是游标 结果集,结果集就是select查询之后返回的所有行数据的集合. 游标则是处理结果集的一种机制吧,它可以定位到结果集中的某一行,多数据进行读写,也可以移动游标定位到你所需要的行中进行操作数据 ...