【最简单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) 高层模块不依赖于低层模块的实现,而低层模块依赖于高层模块定义的接口,通俗的讲,就是高层模块定义接口,低层模块负责实现,这在我们实际开发中经常被用到,层与层之间引用,经 ...
随机推荐
- window共享linux下的文件 samba
1.在Ubuntu上安装samba服务 sudo apt-get install samba 2.修改配置文件vim /etc/samba/smb.conf [xubu] (共享名) guest ac ...
- Android Activity的加载的模式
---恢复内容开始--- 本文来自http://www.cnblogs.com/lwbqqyumidi/p/3771542.html launchMode在多个Activity跳转的过程中扮演着重要的 ...
- r-cnn学习(六):RPN及AnchorTargetLayer学习
RPN网络是faster与fast的主要区别,输入特征图,输出region proposals以及相应的分数. # ------------------------------------------ ...
- 获取FIle路径下所有文件的地址和名称
public static void getFileName(File[] files) { String address=""; if (files != null)// 先判断 ...
- How to install OpenBazaar Server in CentOS7
helps from: https://github.com/OpenBazaar/OpenBazaar-Server http://stackoverflow.com/questions/24917 ...
- python merry -- error handling in the real world
参考: https://www.youtube.com/watch?v=8kTlzR4HhWo https://github.com/miguelgrinberg/merry 背景 本文实际就是把 d ...
- Scrapy003-项目流程
Scrapy003-项目流程 @(Spider)[POSTS] 前两篇文章我们了解到Scrapy的原理和安装的相关知识,这节就需要知道创建项目流程的小知识. 根据官方文档:http://scrapy- ...
- virtualbox
下载安装:http://wiki.centos.org/zh/HowTos/Virtualization/VirtualBox
- 跨域调用webapi
web端跨域调用webapi 在做Web开发中,常常会遇到跨域的问题,到目前为止,已经有非常多的跨域解决方案. 通过自己的研究以及在网上看了一些大神的博客,写了一个Demo 首先新建一个webap ...
- Zookeeper分布式集群搭建
实验条件:3台安装linux的机子,配置好Java环境. 步骤1:下载并分别解包到每台机子的/home/iHge2k目录下,附上下载地址:http://mirrors.cnnic.cn/apache/ ...