您的位置:首页 > 其它

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

2013-06-24 14:42 766 查看
前面DebugLZQ的两篇博文:

浅谈IOC--说清楚IOC是什么

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随笔索引
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: