【最简单IOC容器实现】实现一个最简单的IOC容器
前面DebugLZQ的两篇博文:
IoC Container Benchmark - Performance comparison
在浅谈IOC--说清楚IOC是什么中,DebugLZQ介绍了什么是到底什么是IOC/DI,再复习一下,那么到底什么是IOC呢?
就像Martin Flower所说的:依赖对象的获得过程被反转了,即由之前是consumer主动去new,变成consumer向IOC容器去要,然后由IOC容器注入依赖对象。
这个回答非常的口语化,但IOC的思想确实就是这样。
然后IoC Container Benchmark - Performance comparison对各种现有的IOC容器的性能做了比较。
感兴趣的博友可以看下前面的两篇博文,会有助于你理解这一篇。
-------------------------------------------------------------------------------------------
下面我们就来实现一个最简单的IOC容器。
我们要做的事情是:从一个地方获得输入,然后将这个输入再Write出去。
作为对比,首先来看一下不用IOC,即consumer主动去new的情况:
using System; namespace Normal
{
public interface IReader
{
string Read();
} public interface IWriter
{
void Write(string data);
} public class ReadKeyboard : IReader
{
public string Read()
{
return "code to read from keyboard and return as string";
}
} public class ReadScanner : IReader
{
public string Read()
{
return "code to read from scanner and return as string";
}
} public class WritePrinter : IWriter
{
public void Write(string data)
{
Console.WriteLine("code to write to the printer: "+data);
}
} public class WriteFlashDisk : IWriter
{
public void Write(string data)
{
Console.WriteLine("code to write to the flash disk: " + data);
}
} public class Copy
{
public void DoWork(IReader reader,IWriter writer)
{
string data = reader.Read();
writer.Write(data);
}
} class Test
{
static void Main(string[] args)
{
Copy copy = new Copy();
copy.DoWork(new ReadKeyboard(),new WritePrinter());//依赖对象获得时多个依赖,应当用IOCContainer解耦
}
}
}
撇去Test类不看,以上代码堪称clean,完美符合SOLID(FxCop、StyleCop等默认rules就不谈了)。
Test类依然是"new对象",这是问题的所在。
---------------------------------------------------------------
进入正题:实现这个最简单的IOC容器!
为上面的代码添加一个自定义的IOC容器类,其核心思想是由反射去构建依赖对象。
这个IOC容器的代码如下:
/// <summary>
/// My own IocContainer
/// http://www.cnblogs.com/DebugLZQ
/// </summary>
public class Container
{
private Dictionary<Type, Type> iocMap = new Dictionary<Type, Type>(); public void Register<TypetoResolve, ResolvedType>()
{
if (iocMap.ContainsKey(typeof(TypetoResolve)))
{
throw new Exception(string.Format("Type {0} already registered.", typeof(TypetoResolve).FullName));
}
iocMap.Add(typeof(TypetoResolve), typeof(ResolvedType));
} public T Resolve<T>()
{
return (T)Resolve(typeof(T));
} public object Resolve(Type typeToResolve)
{
//Find the registered type for typeToResolve
if (!iocMap.ContainsKey(typeToResolve))
{
throw new Exception(string.Format("Can't resolve type {0}. Type is not registered.", typeToResolve.FullName));
}
Type resolvedType = iocMap[typeToResolve];
//Try to construct the object
//step-1: find the constructor.
ConstructorInfo ctorInfo = resolvedType.GetConstructors().First();
//step-2:find the parameters for the constructor and try to resolve those.
List<ParameterInfo> paramsInfo = ctorInfo.GetParameters().ToList();
List<object> resolvedParams = new List<object>();
foreach (ParameterInfo param in paramsInfo)
{
Type t = param.ParameterType;
object res = Resolve(t);
resolvedParams.Add(res);
}
//step-3:using reflection invoke constructor to create the object
object retObject = ctorInfo.Invoke(resolvedParams.ToArray());
return retObject;
}
}
--------------------------------------------------------------------
其他部分保持不变(Test类除外)。
如何使用这个IOC容器呢?
给出完整代码,如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection; namespace ContainerDI2_Test
{
public interface IReader
{
string Read();
} public interface IWriter
{
void Write(string data);
} public class ReadKeyboard : IReader
{
public string Read()
{
return "code to read from keyboard and return as string";
}
} public class ReadScanner : IReader
{
public string Read()
{
return "code to read from scanner and return as string";
}
} public class WritePrinter : IWriter
{
public void Write(string data)
{
Console.WriteLine("code to write to the printer: " + data);
}
} public class WriteFlashDisk : IWriter
{
public void Write(string data)
{
Console.WriteLine("code to write to the flash disk: " + data);
}
} public class Copy
{
public void DoWork(IReader reader, IWriter writer)
{
string data = reader.Read();
writer.Write(data);
}
} /// <summary>
/// My own IocContainer
/// http://www.cnblogs.com/DebugLZQ
/// </summary>
public class Container
{
private Dictionary<Type, Type> iocMap = new Dictionary<Type, Type>(); public void Register<TypetoResolve, ResolvedType>()
{
if (iocMap.ContainsKey(typeof(TypetoResolve)))
{
throw new Exception(string.Format("Type {0} already registered.", typeof(TypetoResolve).FullName));
}
iocMap.Add(typeof(TypetoResolve), typeof(ResolvedType));
} public T Resolve<T>()
{
return (T)Resolve(typeof(T));
} public object Resolve(Type typeToResolve)
{
//Find the registered type for typeToResolve
if (!iocMap.ContainsKey(typeToResolve))
{
throw new Exception(string.Format("Can't resolve type {0}. Type is not registered.", typeToResolve.FullName));
}
Type resolvedType = iocMap[typeToResolve];
//Try to construct the object
//step-1: find the constructor.
ConstructorInfo ctorInfo = resolvedType.GetConstructors().First();
//step-2:find the parameters for the constructor and try to resolve those.
List<ParameterInfo> paramsInfo = ctorInfo.GetParameters().ToList();
List<object> resolvedParams = new List<object>();
foreach (ParameterInfo param in paramsInfo)
{
Type t = param.ParameterType;
object res = Resolve(t);
resolvedParams.Add(res);
}
//step-3:using reflection invoke constructor to create the object
object retObject = ctorInfo.Invoke(resolvedParams.ToArray());
return retObject;
}
} class Test
{
static void DIRegistration(Container container)
{
container.Register<IReader, ReadKeyboard>();
container.Register<IWriter, WritePrinter>();
} static void Main(string[] args)
{
Container container = new Container();
DIRegistration(container); Copy copy = new Copy();
copy.DoWork(container.Resolve<IReader>(), container.Resolve<IWriter>());//依赖于IOC容器了
}
}
}
可以和前面new那个代码比较下,看看两者的区别。
-----------------------------------------------------------------
当然DebugLZQ为了更清楚的说明代码问题,因而没有进行分层,当然你也可以把工程写成这样:
分层的好处是结构清晰。
以上两段代码,均可正常运行,运行结果相同,其运行截图如下:
--------------------------------------------------------------------------------
当然根据个人编码习惯,我们也可以把Copy类写成这样
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection; namespace ContainerDI
{
public interface IReader
{
string Read();
} public interface IWriter
{
void Write(string data);
} public class ReadKeyboard : IReader
{
public string Read()
{
return "code to read from keyboard and return as string";
}
} public class ReadScanner : IReader
{
public string Read()
{
return "code to read from scanner and return as string";
}
} public class WritePrinter : IWriter
{
public void Write(string data)
{
Console.WriteLine("code to write to the printer: " + data);
}
} public class WriteFlashDisk : IWriter
{
public void Write(string data)
{
Console.WriteLine("code to write to the flash disk: " + data);
}
} public class Copy
{
private Container _container;
private IReader _reader;
private IWriter _writer;//也可以是具体类型 public Copy(Container container)
{
this._container = container;
this._reader = _container.Resolve<IReader>();
this._writer = _container.Resolve<IWriter>();
} public void DoWork()
{
string data = _reader.Read();
_writer.Write(data);
}
} /// <summary>
/// My own IocContainer
/// </summary>
public class Container
{
private Dictionary<Type, Type> iocMap = new Dictionary<Type, Type>(); public void Register<TypetoResolve, ResolvedType>()
{
if (iocMap.ContainsKey(typeof(TypetoResolve)))
{
throw new Exception(string.Format("Type {0} already registered.",typeof(TypetoResolve).FullName));
}
iocMap.Add(typeof(TypetoResolve), typeof(ResolvedType));
} public T Resolve<T>()
{
return (T)Resolve(typeof(T));
} public object Resolve(Type typeToResolve)
{
//Find the registered type for typeToResolve
if(!iocMap.ContainsKey(typeToResolve))
{
throw new Exception(string.Format("Can't resolve type {0}. Type is not registered.",typeToResolve.FullName));
}
Type resolvedType = iocMap[typeToResolve];
//Try to construct the object
//step-1: find the constructor.
ConstructorInfo ctorInfo = resolvedType.GetConstructors().First();
//step-2:find the parameters for the constructor and try to resolve those.
List<ParameterInfo> paramsInfo = ctorInfo.GetParameters().ToList();
List<object> resolvedParams = new List<object>();
foreach (ParameterInfo param in paramsInfo)
{
Type t = param.ParameterType;
object res = Resolve(t);
resolvedParams.Add(res);
}
//step-3:using reflection invoke constructor to create the object
object retObject = ctorInfo.Invoke(resolvedParams.ToArray());
return retObject;
}
} class Test
{
static void DIRegistration(Container container)
{
container.Register<IReader, ReadKeyboard>();
container.Register<IWriter, WritePrinter>();
} static void Main(string[] args)
{
Container container = new Container();
DIRegistration(container); Copy copy = new Copy(container);
copy.DoWork();
}
}
}
也可以写成这样:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection; namespace ContainerDI2
{
public interface IReader
{
string Read();
} public interface IWriter
{
void Write(string data);
} public class ReadKeyboard : IReader
{
public string Read()
{
return "code to read from keyboard and return as string";
}
} public class ReadScanner : IReader
{
public string Read()
{
return "code to read from scanner and return as string";
}
} public class WritePrinter : IWriter
{
public void Write(string data)
{
Console.WriteLine("code to write to the printer: " + data);
}
} public class WriteFlashDisk : IWriter
{
public void Write(string data)
{
Console.WriteLine("code to write to the flash disk: " + data);
}
} public class Copy
{
private IReader _reader;
private IWriter _writer;//也可以是具体类型 public Copy(IReader reader,IWriter writer)
{ this._reader = reader;
this._writer = writer;
} public void DoWork()
{
string data = _reader.Read();
_writer.Write(data);
}
} /// <summary>
/// My own IocContainer
/// http://www.cnblogs.com/DebugLZQ
/// </summary>
public class Container
{
private Dictionary<Type, Type> iocMap = new Dictionary<Type, Type>(); public void Register<TypetoResolve, ResolvedType>()
{
if (iocMap.ContainsKey(typeof(TypetoResolve)))
{
throw new Exception(string.Format("Type {0} already registered.", typeof(TypetoResolve).FullName));
}
iocMap.Add(typeof(TypetoResolve), typeof(ResolvedType));
} public T Resolve<T>()
{
return (T)Resolve(typeof(T));
} public object Resolve(Type typeToResolve)
{
//Find the registered type for typeToResolve
if (!iocMap.ContainsKey(typeToResolve))
{
throw new Exception(string.Format("Can't resolve type {0}. Type is not registered.", typeToResolve.FullName));
}
Type resolvedType = iocMap[typeToResolve];
//Try to construct the object
//step-1: find the constructor.
ConstructorInfo ctorInfo = resolvedType.GetConstructors().First();
//step-2:find the parameters for the constructor and try to resolve those.
List<ParameterInfo> paramsInfo = ctorInfo.GetParameters().ToList();
List<object> resolvedParams = new List<object>();
foreach (ParameterInfo param in paramsInfo)
{
Type t = param.ParameterType;
object res = Resolve(t);
resolvedParams.Add(res);
}
//step-3:using reflection invoke constructor to create the object
object retObject = ctorInfo.Invoke(resolvedParams.ToArray());
return retObject;
}
} class Test
{
static void DIRegistration(Container container)
{
container.Register<IReader, ReadKeyboard>();
container.Register<IWriter, WritePrinter>();
} static void Main(string[] args)
{
Container container = new Container();
DIRegistration(container); Copy copy = new Copy(container.Resolve<IReader>(),container.Resolve<IWriter>());
copy.DoWork();
}
}
}
程序均可正常工作,运行结果与前面相同。
至此,最简单的IOC容器完成。
----------------------------------------------------------------------
-----------------------------------------------------------------------
上面的IOC容器,每次都重新构建一个新的对象。
我们稍加改造:
如果我们需要维持对象对象的某个状态(如对象的创建时刻等),也就是说给给IOC容器构建的对象加上生命周期。那我们该怎么办呢?
思路很简单:对于需要维持状态的对象,IOC第一次构建以后,先存着,后来有要用的直接返回前面构建的那个。
把我们的思路转换成代码,如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection; namespace ContainerDIWithLifeOption
{
public interface IReader
{
string Read();
} public interface IWriter
{
void Write(string data);
} public class ReadKeyboard : IReader
{
public string Read()
{
return "code to read from keyboard and return as string";
}
} public class ReadScanner : IReader
{
public string Read()
{
return "code to read from scanner and return as string";
}
} public class WritePrinter : IWriter
{
public void Write(string data)
{
Console.WriteLine("code to write to the printer: " + data);
}
} public class WriteFlashDisk : IWriter
{
public void Write(string data)
{
Console.WriteLine("code to write to the flash disk: " + data);
}
} public class Copy
{
private IReader _reader;
private IWriter _writer;//也可以是具体类型 public Copy(IReader reader, IWriter writer)
{ this._reader = reader;
this._writer = writer;
} public void DoWork()
{
string data = _reader.Read();
_writer.Write(data);
}
} public enum LifeTimeOptions
{
TransientLifeTimeOption,
ContainerControlledLifeTimeOption
} public class ResolvedTypeWithLifeTimeOptions
{
public Type ResolvedType { get; set; }
public LifeTimeOptions LifeTimeOption { get; set; }
public object InstanceValue { get; set; } public ResolvedTypeWithLifeTimeOptions(Type resolvedType)
{
ResolvedType = resolvedType;
LifeTimeOption = LifeTimeOptions.TransientLifeTimeOption;
InstanceValue = null;
} public ResolvedTypeWithLifeTimeOptions(Type resolvedType, LifeTimeOptions lifeTimeOption)
{
ResolvedType = resolvedType;
LifeTimeOption = lifeTimeOption;
InstanceValue = null;
}
} public class Container
{
private Dictionary<Type, ResolvedTypeWithLifeTimeOptions>
iocMap = new Dictionary<Type, ResolvedTypeWithLifeTimeOptions>(); public void Register<T1, T2>()
{
Register<T1, T2>(LifeTimeOptions.TransientLifeTimeOption);
} public void Register<T1, T2>(LifeTimeOptions lifeTimeOption)
{
if (iocMap.ContainsKey(typeof (T1)))
{
throw new Exception(string.Format("Type {0} already registered.", typeof (T1).FullName));
}
ResolvedTypeWithLifeTimeOptions targetType = new ResolvedTypeWithLifeTimeOptions(typeof (T2),
lifeTimeOption);
iocMap.Add(typeof (T1), targetType);
} public T Resolve<T>()
{
return (T) Resolve(typeof (T));
} public object Resolve(Type typeToResolve)
{
// Find the registered type for typeToResolve
if (!iocMap.ContainsKey(typeToResolve))
throw new Exception(string.Format("Can't resolve {0}.Type is not registered.", typeToResolve.FullName)); ResolvedTypeWithLifeTimeOptions resolvedType = iocMap[typeToResolve]; // Step-1: If LifeTimeOption is ContainerControlled and there is
//already an instance created then return the created instance.
if (resolvedType.LifeTimeOption ==
LifeTimeOptions.ContainerControlledLifeTimeOption&&
resolvedType.InstanceValue != null)
return resolvedType.InstanceValue; // Try to construct the object
// Step-2: find the constructor
//(ideally first constructor if multiple constructors present for the type)
ConstructorInfo ctorInfo = resolvedType.ResolvedType.GetConstructors().First(); // Step-3: find the parameters for the constructor and try to resolve those
List<ParameterInfo> paramsInfo = ctorInfo.GetParameters().ToList();
List<object> resolvedParams = new List<object>();
foreach (ParameterInfo param in paramsInfo)
{
Type t = param.ParameterType;
object res = Resolve(t);
resolvedParams.Add(res);
} // Step-4: using reflection invoke constructor to create the object
object retObject = ctorInfo.Invoke(resolvedParams.ToArray()); resolvedType.InstanceValue = retObject; return retObject;
}
} class Program
{
static void Main(string[] args)
{
Container container = new Container(); DIRegistration(container); Copy copy = new Copy(container.Resolve<IReader>(),container.Resolve<IWriter>());
copy.DoWork();
Console.ReadLine(); Copy copy2 = new Copy(container.Resolve<IReader>(), container.Resolve<IWriter>());
copy2.DoWork();
Console.ReadLine();
} private static void DIRegistration(Container container)
{
container.Register<IReader, ReadKeyboard>(LifeTimeOptions.ContainerControlledLifeTimeOption);
container.Register<IWriter, WritePrinter>(LifeTimeOptions.TransientLifeTimeOption);
}
}
}
程序运行如下:
至此,正文完结。
参考:
- CSDN: 架构师之路(39)---IoC框架
- EagleFish(邢瑜琨) 深度理解依赖注入(Dependence Injection)
- Martin Flower Inversion of Control Containers and the Dependency Injection pattern
- 黄忠成 Object Builder Application Block
- Dependency Inversion Principle, IoC Container & Dependency Injection (Part - 1)
- Dependency Inversion Principle, IoC Container & Dependency Injection (Part - 2)
- Dependency Inversion Principle, IoC Container, and Dependency Injection (Part - 3)
- Dependency Inversion Principle, IoC Container, and Dependency Injection (Part - 4)
- http://www.codeproject.com/Articles/538536/A-curry-of-Dependency-Inversion-Principle-DIP-Inve
- http://www.jamesshore.com/Blog/Dependency-Injection-Demystified.html
- http://www.pluralsight.com/training
- http://www.oodesign.com
- http://blog.architexa.com/2010/05/types-of-dependency-injection/
- http://www.codeproject.com/Articles/495019/Dependency-Inversion-Principle-and-the-Dependency
- and so on
忽然想起一句话,不知是在哪里看到的了,大概意思是:不能用自己语言讲清楚的东西---你没掌握,技术这个东西不存在所谓的“只可意会不可言传”。
希望对你有帮助,老鸟、大虾、高手等绕过,轻拍~
Update:请关注后续博文:MEF随笔索引
【最简单IOC容器实现】实现一个最简单的IOC容器的更多相关文章
- IoC原理-使用反射/Emit来实现一个最简单的IoC容器
从Unity到Spring.Net,到Ninject,几年来陆陆续续用过几个IoC框架.虽然会用,但也没有一直仔细的研究过IoC实现的过程.最近花了点时间,下了Ninject的源码,研究了一番,颇有收 ...
- 手写一个最简单的IOC容器,从而了解spring的核心原理
从事开发工作多年,spring源码没有特意去看过.但是相关技术原理倒是背了不少,毕竟面试的那关还是得过啊! 正所谓面试造火箭,工作拧螺丝.下面实现一个最简单的ioc容器,供大家参考. 1.最终结果 2 ...
- 【spring】-- 手写一个最简单的IOC框架
1.什么是springIOC IOC就是把每一个bean(实体类)与bean(实体了)之间的关系交给第三方容器进行管理. 如果我们手写一个最最简单的IOC,最终效果是怎样呢? xml配置: <b ...
- 造轮子:实现一个简易的 Spring IoC 容器
作者:DeppWang.原文地址 我通过实现一个简易的 Spring IoC 容器,算是入门了 Spring 框架.本文是对实现过程的一个总结提炼,需要配合源码阅读,源码地址. 结合本文和源码,你应该 ...
- IoC容器Autofac正篇之简单实例
先上一段代码. namespace ConsoleApplication3 { class Program { static void Main(string[] args) { ContainerB ...
- IoC容器Autofac正篇之简单实例(四)
先上一段代码. namespace ConsoleApplication3 { class Program { static void Main(string[] args) { ContainerB ...
- Unity容器在asp.net mvc中的IOC应用及AOP应用
<asp.net-mvc框架揭秘>一书中,有个示例,是使用unity容器来注入自定义的控制器工厂.代码示例可以自己去下载源码,在这里我就不说了.IOC容器的本质是解耦的实例化接口类,而如何 ...
- Ioc 器管理的应用程序设计,前奏:容器属于哪里? 控制容器的反转和依赖注入模式
Ioc 器管理的应用程序设计,前奏:容器属于哪里? 我将讨论一些我认为应该应用于“容器管理”应用程序设计的原则. 模式1:服务字典 字典或关联数组是我们在软件工程中学到的第一个构造. 很容易看到使 ...
- Lind.DDD.IoC(大叔推荐)~在服务定位器中引入IoC容器~容器的适配器
回到目录 关于依赖倒置(DIP) 高层模块不依赖于低层模块的实现,而低层模块依赖于高层模块定义的接口,通俗的讲,就是高层模块定义接口,低层模块负责实现,这在我们实际开发中经常被用到,层与层之间引用,经 ...
随机推荐
- struts标签内容截取
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles"%> <% ...
- MySQL 5.6 for Windows 解压缩版配置安装
MySQL安装文件分为两种,一种是msi格式的,一种是zip格式的.如果是msi格式的可以直接点击安装,按照它给出的安装提示进行安装(相信大家的英文可以看懂英文提示),一般MySQL将会安装在C:\P ...
- iOS 引入framework的常见问题和原理
今天在引入第三方framework时,我按照以前的方法,把framework加入到了下图的地方: 默认是required的,之后程序就crash了,报错dyld: Library not loaded ...
- js颠倒数组元素顺序reverse()
颠倒数组元素顺序reverse() reverse() 方法用于颠倒数组中元素的顺序. 语法: arrayObject.reverse() 注意:该方法会改变原来的数组,而不会创建新的数组. 定义数组 ...
- hibernate中HQL多对多的查询
现有三张表 TLXPURCHASE.采购事项审批表,TLXPURCHASEACTIVITY.采购招标活动对应表,TLXACTIVITY.招标活动表,采购事项审批表和采购活动表是多对多关系.java中定 ...
- CAEmitterLayer实现雪花效果
CAEmitterLayer 简介 在iOS5.0中,Apple引入了CAEmitterLayer层,CAEmitterLayer是一个高性能的粒子效果引擎,被用来创建实时粒子动画,如:烟雾,火,雨等 ...
- 计算机网络中的帧封装(C实现)
这段时间开始复习计算机网络,看到帧封装这一节,结合以前的课程设计,就用C写了个帧封装的程序,说实话C学的确实不怎么样,实现的时候对于文件操作那部分查了好多资料,下面说说帧封装是啥情况. 学过计算机网络 ...
- ngx_image_thumb模块生成缩略图
ngx_image_thumb是nginx中用来生成缩略图的模块. 编译前确定已经安装了libcurl-dev libgd2-dev libpcre-dev gd-devel pcre-devel l ...
- 初学微信小程序
最近微信推出了微信小程序,为此我学了几天,基本了解了组件及简单语法,但是今天我自己想要独立写一个demo时,忽然发现难道我的不是微信小程序的语法(我以前是iOS 开发,不用css),而是css样式的设 ...
- javascript将object转string字符串
var jsonData = {a:1,b:2}; function obj2string(o) { var r = []; if (typeof o == "string") { ...