随着大规模的项目越来越多,许多项目都引入了依赖注入框架,其中最流行的有 Castle Windsor, Autofac 和 Unity Container。
微软在最新版的 Asp.Net Core 中自带了依赖注入的功能,有兴趣可以。
关于什么是依赖注入容器网上已经有很多的文章介绍,这里我将重点讲述如何实现一个自己的容器,可以帮助你理解依赖注入的原理。
在编写容器之前,应该先想好这个容器如何使用。
容器允许注册服务和实现类型,允许从服务类型得出服务的实例,它的使用代码应该像
- var container = new Container();
- container.Register < MyLogger,
- ILogger > ();
- var logger = container.Resolve < ILogger > ();
在上面的构想中,Container 类有两个函数,一个是
,一个是
- Register
。 容器需要在
- Resolve
时关联
- Register
接口到
- ILogger
实现,并且需要在
- MyLogger
时知道应该为
- Resolve
生成
- ILogger
的实例。 以下是实现这两个函数最基础的代码
- MyLogger
- public class Container {
- // service => implementation
- private IDictionary < Type,
- Type > TypeMapping {
- get;
- set;
- }
- public Container() {
- TypeMapping = new Dictionary < Type,
- Type > ();
- }
- public void Register < TImplementation,
- TService > () where TImplementation: TService {
- TypeMapping[typeof(TService)] = typeof(TImplementation);
- }
- public TService Resolve < TService > () {
- var implementationType = TypeMapping[typeof(TService)];
- return (TService) Activator.CreateInstance(implementationType);
- }
- }
在内部创建了一个服务类型 (接口类型) 到实现类型的索引,
- Container
时使用索引找到实现类型并创建实例。 这个实现很简单,但是有很多问题,例如
- Resolve
要让一个服务类型对应多个实现类型,可以把
改为
- TypeMapping
- IDictionary < Type,
- IList < Type >> TypeMapping {
- get;
- set;
- }
如果另外提供一个保存实例的变量,也能实现生命周期管理,但显得稍微复杂了。
这里可以转换一下思路,把 {服务类型 => 实现类型}改为 {服务类型 => 工厂函数},让生命周期的管理在工厂函数中实现。
- IDictionary < Type,
- IList < Func < object >>> Factories {
- get;
- set;
- }
有时候我们会想让用户在配置文件中切换实现类型,这时如果把键类型改成服务类型 + 字符串,实现起来会简单很多。
Resolve 可以这样用:
- Resolve<Service>(serviceKey: Configuration["ImplementationName"])
- IDictionary < Tuple < Type,
- string > ,
- IList < Func < object >>> Factories {
- get;
- set;
- }
在确定了索引类型后,
和
- Register
的处理都应该随之改变。
- Resolve
注册时应该首先根据实现类型生成工厂函数,再把工厂函数加到服务类型对应的列表中。
- Register
解决时应该根据服务类型找到工厂函数,然后执行工厂函数返回实例。
- Resolve
这个容器新增了一个
函数,用于解决多个实例。 另外还用了
- ResolveMany
编译工厂函数,生成效率会比
- Expression.Lambda
快数十倍。
- Activator.CreateInstance
- public class Container {
- private IDictionary < Tuple < Type,
- string > ,
- IList < Func < object >>> Factories {
- get;
- set;
- }
- public Container() {
- Factories = new Dictionary < Tuple < Type,
- string > ,
- IList < Func < object >>> ();
- }
- public void Register < TImplementation,
- TService > (string serviceKey = null) where TImplementation: TService {
- var key = Tuple.Create(typeof(TService), serviceKey);
- IList < Func < object >> factories;
- if (!Factories.TryGetValue(key, out factories)) {
- factories = new List < Func < object >> ();
- Factories[key] = factories;
- }
- var factory = Expression.Lambda < Func < object >> (Expression.New(typeof(TImplementation))).Compile();
- factories.Add(factory);
- }
- public TService Resolve < TService > (string serviceKey = null) {
- var key = Tuple.Create(typeof(TService), serviceKey);
- var factory = Factories[key].Single();
- return (TService) factory();
- }
- public IEnumerable < TService > ResolveMany < TService > (string serviceKey = null) {
- var key = Tuple.Create(typeof(TService), serviceKey);
- IList < Func < object >> factories;
- if (!Factories.TryGetValue(key, out factories)) {
- yield
- break;
- }
- foreach(var factory in factories) {
- yield
- return (TService) factory();
- }
- }
- }
改进后的容器仍然有以下的问题
以下面代码为例
- var logger_a = container.Resolve < ILogger > ();
- var logger_b = container.Resolve < ILogger > ();
使用上面的容器执行这段代码时,
和
- logger_a
是两个不同的对象,如果想要每次
- logger_b
都返回同样的对象呢? 我们可以对工厂函数进行包装,借助闭包 (Closure) 的力量可以非常简单的实现。
- Resolve
- private Func < object > WrapFactory(Func < object > originalFactory, bool singleton) {
- if (!singleton) return originalFactory;
- object value = null;
- return () = >{
- if (value == null) value = originalFactory();
- return value;
- };
- }
添加这个函数后在
中调用
- Register
即可。 完整代码将在后面放出,接下来再看如何实现构造函数注入。
- factory = WrapFactory(factory, singleton);
以下面代码为例
- public class MyLogWriter: ILogWriter {
- public void Write(string str) {
- Console.WriteLine(str);
- }
- }
- public class MyLogger: ILogger {
- ILogWriter _writer;
- public MyLogger(ILogWriter writer) {
- _writer = writer;
- }
- public void Log(string message) {
- _writer.Write("[ Log ] " + message);
- }
- }
- static void Main(string[] args) {
- var container = new Container();
- container.Register < MyLogWriter,
- ILogWriter > ();
- container.Register < MyLogger,
- ILogger > ();
- var logger = container.Resolve < ILogger > ();
- logger.Log("Example Message");
- }
在这段代码中,
构造时需要一个
- MyLogger
的实例,但是这个实例我们不能直接传给它。 这样就要求容器可以自动生成
- ILogWriter
的实例,再传给
- ILogWriter
以生成
- MyLogger
的实例。 要实现这个功能需要使用 c# 中的反射机制。
- MyLogger
把上面代码中的
- var factory = Expression.Lambda < Func < object >> (Expression.New(typeof(TImplementation))).Compile();
换成
- private Func < object > BuildFactory(Type type) {
- // 获取类型的构造函数
- var constructor = type.GetConstructors().FirstOrDefault();
- // 生成构造函数中的每个参数的表达式
- var argumentExpressions = new List < Expression > ();
- foreach(var parameter in constructor.GetParameters()) {
- var parameterType = parameter.ParameterType;
- if (parameterType.IsGenericType && parameterType.GetGenericTypeDefinition() == typeof(IEnumerable < >)) {
- // 等于调用this.ResolveMany<TParameter>();
- argumentExpressions.Add(Expression.Call(Expression.Constant(this), "ResolveMany", parameterType.GetGenericArguments(), Expression.Constant(null, typeof(string))));
- } else {
- // 等于调用this.Resolve<TParameter>();
- argumentExpressions.Add(Expression.Call(Expression.Constant(this), "Resolve", new[] {
- parameterType
- },
- Expression.Constant(null, typeof(string))));
- }
- }
- // 构建new表达式并编译到委托
- var newExpression = Expression.New(constructor, argumentExpressions);
- return Expression.Lambda < Func < object >> (newExpression).Compile();
- }
这段代码通过反射获取了构造函数中的所有参数,并对每个参数使用
或
- Resolve
解决。
- ResolveMany
的时候才会构建
- MyLogger
,这样做的好处是注入的实例不一定需要是单例。 用表达式构建的工厂函数解决的时候的性能会很高。
- MyLogWriter
容器和示例的完整代码如下
- public interface ILogWriter {
- void Write(string text);
- }
- public class MyLogWriter: ILogWriter {
- public void Write(string str) {
- Console.WriteLine(str);
- }
- }
- public interface ILogger {
- void Log(string message);
- }
- public class MyLogger: ILogger {
- ILogWriter _writer;
- public MyLogger(ILogWriter writer) {
- _writer = writer;
- }
- public void Log(string message) {
- _writer.Write("[ Log ] " + message);
- }
- }
- static void Main(string[] args) {
- var container = new Container();
- container.Register < MyLogWriter,
- ILogWriter > ();
- container.Register < MyLogger,
- ILogger > ();
- var logger = container.Resolve < ILogger > ();
- logger.Log("asdasdas");
- }
- public class Container {
- private IDictionary < Tuple < Type,
- string > ,
- IList < Func < object >>> Factories {
- get;
- set;
- }
- public Container() {
- Factories = new Dictionary < Tuple < Type,
- string > ,
- IList < Func < object >>> ();
- }
- private Func < object > WrapFactory(Func < object > originalFactory, bool singleton) {
- if (!singleton) return originalFactory;
- object value = null;
- return () = >{
- if (value == null) value = originalFactory();
- return value;
- };
- }
- private Func < object > BuildFactory(Type type) {
- // 获取类型的构造函数
- var constructor = type.GetConstructors().FirstOrDefault();
- // 生成构造函数中的每个参数的表达式
- var argumentExpressions = new List < Expression > ();
- foreach(var parameter in constructor.GetParameters()) {
- var parameterType = parameter.ParameterType;
- if (parameterType.IsGenericType && parameterType.GetGenericTypeDefinition() == typeof(IEnumerable < >)) {
- // 等于调用this.ResolveMany<TParameter>();
- argumentExpressions.Add(Expression.Call(Expression.Constant(this), "ResolveMany", parameterType.GetGenericArguments(), Expression.Constant(null, typeof(string))));
- } else {
- // 等于调用this.Resolve<TParameter>();
- argumentExpressions.Add(Expression.Call(Expression.Constant(this), "Resolve", new[] {
- parameterType
- },
- Expression.Constant(null, typeof(string))));
- }
- }
- // 构建new表达式并编译到委托
- var newExpression = Expression.New(constructor, argumentExpressions);
- return Expression.Lambda < Func < object >> (newExpression).Compile();
- }
- public void Register < TImplementation,
- TService > (string serviceKey = null, bool singleton = false) where TImplementation: TService {
- var key = Tuple.Create(typeof(TService), serviceKey);
- IList < Func < object >> factories;
- if (!Factories.TryGetValue(key, out factories)) {
- factories = new List < Func < object >> ();
- Factories[key] = factories;
- }
- var factory = BuildFactory(typeof(TImplementation));
- WrapFactory(factory, singleton);
- factories.Add(factory);
- }
- public TService Resolve < TService > (string serviceKey = null) {
- var key = Tuple.Create(typeof(TService), serviceKey);
- var factory = Factories[key].Single();
- return (TService) factory();
- }
- public IEnumerable < TService > ResolveMany < TService > (string serviceKey = null) {
- var key = Tuple.Create(typeof(TService), serviceKey);
- IList < Func < object >> factories;
- if (!Factories.TryGetValue(key, out factories)) {
- yield
- break;
- }
- foreach(var factory in factories) {
- yield
- return (TService) factory();
- }
- }
- }
这个容器实现了一个依赖注入容器应该有的主要功能,但是还是有很多不足的地方,例如
我在中也使用了自己编写的容器,只有 300 多行但是可以满足实际项目的使用。
完整的源代码可以。
微软从. Net Core 开始提供了的抽象接口,这为依赖注入提供了一个标准。
在将来可能不会再需要学习 Castle Windsor, Autofac 等,而是直接使用微软提供的标准接口。
虽然具体的实现方式离我们原来越远,但是了解一下它们的原理总是有好处的。
来源: http://www.cnblogs.com/zkweb/p/5867820.html