如何编写一个简单的依赖注入容器
随着大规模的项目越来越多,许多项目都引入了依赖注入框架,其中最流行的有Castle Windsor,Autofac和Unity Container。 容器的构想在编写容器之前,应该先想好这个容器如何使用。 var container = new Container();
container.Register<MyLogger,ILogger>();var logger = container.Resolve<ILogger>();
最基础的容器在上面的构想中,Container类有两个函数,一个是 public class Container{ ? ?// service => implementation
? ?private IDictionary<Type,Type> TypeMapping { get; set; } ? ?public Container() ? ?{
? ? ? ?TypeMapping = new Dictionary<Type,Type>();
? ?} ? ?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);
? ?}
}
改进容器的构想 - 类型索引类型要让一个服务类型对应多个实现类型,可以把 IDictionary<Type,IList<Type>> TypeMapping { set; }
如果另外提供一个保存实例的变量,也能实现生命周期管理,但显得稍微复杂了。 object>>> Factories { 有时候我们会想让用户在配置文件中切换实现类型,这时如果把键类型改成服务类型+字符串,实现起来会简单很多。
改进容器的构想 - Register和Resolve的处理在确定了索引类型后, 改进后的容器这个容器新增了一个 Container{ ? ?private IDictionary<Tuple<Type,21);">Container() ? ?{
? ? ? ?Factories = new Dictionary<Tuple<Type,255);">object>>>();
? ?} ? ?string serviceKey = null) ? ? ? ?where TImplementation : TService
? ?{ ? ? ? ?var key = Tuple.Create(typeof(TService),serviceKey);
? ? ? ?IList<Func<object>> factories; ? ? ? ?if (!Factories.TryGetValue(key,255);">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>(null)
? ?{ ? ? ? ?var factory = Factories[key].Single(); ? ? ? ?return (TService)factory();
? ?} ? ?public IEnumerable<TService> ResolveMany<TService>(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>();
使用上面的容器执行这段代码时, private Func<object> WrapFactory(Func<object> originalFactory,255);">bool singleton){ ? ?if (!singleton) ? ? ? ?return originalFactory; ? ?object value = null; ? ?return () =>
? ?{ ? ? ? ?if (value == null) ? ? ? ? ? ?value = originalFactory(); ? ? ? ?return value;
? ?};
}
添加这个函数后在 实现构造函数注入MyLogWriter : ILogWriter{ ? ?void Write(string str) ? ?{ ? ? ? ?Console.WriteLine(str); ? ?} }MyLogger : ILogger{ ? ?ILogWriter _writer; ? ? ? ?MyLogger(ILogWriter writer) ? ?{ ? ? ? ?_writer = writer; ? ?} ? ? ? ?Log(string message) ? ?{ ? ? ? ?_writer.Write("[ Log ] " + message); ? ?} }static Main(string[] args){ ? ?new Container(); ? ?container.Register<MyLogWriter,ILogWriter>(); ? ?container.Register<MyLogger,ILogger>(); ? ? ? ?var logger = container.Resolve<ILogger>(); ? ?logger.Log("Example Message"); } 在这段代码中, 把上面代码中的 typeof(TImplementation))).Compile(); 换成 BuildFactory(Type type){ ? ?// 获取类型的构造函数
? ?var constructor = type.GetConstructors().FirstOrDefault(); ? ?// 生成构造函数中的每个参数的表达式
? ?var argumentExpressions = new List<Expression>(); ? ?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,255);">typeof(string))));
? ? ? ?} ? ? ? ?else
? ? ? ?{ ? ? ? ? ? ?// 等于调用this.Resolve<TParameter>();
? ? ? ? ? ?argumentExpressions.Add(Expression.Call(
? ? ? ? ? ? ? ?Expression.Constant("Resolve",? ? ? ? ? ? ? ?new [] { parameterType },255);">string))));
? ? ? ?}
? ?} ? ?// 构建new表达式并编译到委托
? ?var newExpression = Expression.New(constructor,argumentExpressions); ? ?return Expression.Lambda<Func<object>>(newExpression).Compile();
}
这段代码通过反射获取了构造函数中的所有参数,并对每个参数使用 完整代码容器和示例的完整代码如下 interface string text);
}ILogger{ ? ?string message);
}ILogger{
? ?ILogWriter _writer; ? ?MyLogger(ILogWriter writer) ? ?{
? ? ? ?_writer = writer;
? ?} ? ?"asdasdas");
}object>>>();
? ?} ? ?bool singleton) ? ?{ ? ? ? ?if (!singleton) ? ? ? ? ? ?return originalFactory; ? ? ? ?null; ? ? ? ?return () =>
? ? ? ?{ ? ? ? ? ? ?null) ? ? ? ? ? ? ? ?value = originalFactory(); ? ? ? ? ? ?value;
? ? ? ?};
? ?} ? ?BuildFactory(Type type) ? ?{ ? ? ? ?// 获取类型的构造函数
? ? ? ?var constructor = type.GetConstructors().FirstOrDefault(); ? ? ? ?// 生成构造函数中的每个参数的表达式
? ? ? ?new List<Expression>(); ? ? ? ?in constructor.GetParameters())
? ? ? ?{ ? ? ? ? ? ?var parameterType = parameter.ParameterType; ? ? ? ? ? ?if (parameterType.IsGenericType &&
? ? ? ? ? ? ? ?parameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
? ? ? ? ? ?{ ? ? ? ? ? ? ? ?// 等于调用this.ResolveMany<TParameter>();
? ? ? ? ? ? ? ?argumentExpressions.Add(Expression.Call(
? ? ? ? ? ? ? ? ? ?Expression.Constant(string))));
? ? ? ? ? ?} ? ? ? ? ? ?else
? ? ? ? ? ?{ ? ? ? ? ? ? ? ?// 等于调用this.Resolve<TParameter>();
? ? ? ? ? ? ? ?argumentExpressions.Add(Expression.Call(
? ? ? ? ? ? ? ? ? ?Expression.Constant(string))));
? ? ? ? ? ?}
? ? ? ?} ? ? ? ?// 构建new表达式并编译到委托
? ? ? ?object>>(newExpression).Compile();
? ?} ? ?bool singleton = false) ? ? ? ?var factory = BuildFactory(typeof(TImplementation));
? ? ? ?WrapFactory(factory,singleton);
? ? ? ?factories.Add(factory);
? ?} ? ?factory();
? ? ? ?}
? ?}
}
写在最后这个容器实现了一个依赖注入容器应该有的主要功能,但是还是有很多不足的地方,例如
我在ZKWeb网页框架中也使用了自己编写的容器,只有300多行但是可以满足实际项目的使用。 微软从.Net Core开始提供了DependencyInjection的抽象接口,这为依赖注入提供了一个标准。 来源:http://www.cnblogs.com/zkweb/archive/2016/09/13/5867820.html (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |