百行代码打造一个DI容器(支持瞬时生命周期、单利生命周期、构造函数自动注入、属性自动注入、字段自动注入)

DI注入在.Net平台是非常流行的, 很多项目都用到了,很多开发人员或多或少也用到DI容器了,感觉DI容器很神奇很厉害。本文将通过百行代码展示DI容器的内部核心代码(包括组件的瞬时生命周期、单利生命周期、构造函数自动注入、属性自动注入、字段自动注入),揭开DI容器的神秘面纱。

 

   一、定义DI容器接口

1:  public interface IContainer
2: 
{
3:      void Register<TService, TComponent>(bool isSingleton = false);//组件注册
4:     
object Resolve(Type type);//解析组件
5: 
}

 
二、定义组件元数据类
1: class ComponentInfo
2:  {
3:      public Type Contact;//组件契约
4:      public Type Implimention;//组件实现类型
5:      public bool IsSingleton;//是否单利
6:  }
 
三、组件注册代码 
01:  //组件元数据
02:    Dictionary<Type, ComponentInfo> Components = new Dictionary<Type, ComponentInfo>();
03:    public void Register<TService, TComponent>(bool isSingleton = false)
04:    {
05:        var serviceType = typeof(TService);
06:        lock (Components)
07:        {
08:            Components[serviceType] = new ComponentInfo
09:            { 
10:                Contact = serviceType
11:                , Implimention = typeof(TComponent)
12:                , IsSingleton = isSingleton
13:            };
14:        }
15:    }
 

四、组件解析代码

01:  public object Resolve(Type serviceType)
02:   {
03:       //
04:       if (serviceType == null)
05:           throw new ArgumentNullException("type");
06:  
07:       ComponentInfo t;
08:       if (!Components.TryGetValue(serviceType, out t))
09:           throw new ApplicationException(string.Format("{0} 没有配置到容器中", serviceType.FullName));
10:  
11:       //如果是单利类型,并且单利容器中存在则直接返回
12:       if (t.IsSingleton && Instances.ContainsKey(serviceType))
13:           return Instances[serviceType];
14:  
15:       //得到构造函数
16:       var ctor = t.Implimention
17:           .GetConstructors()
18:           .OrderByDescending(c => c.GetParameters().Count())//按照构造函数参数的个数进行降序排列
19:           .Where(c => c.GetParameters().TrueForAll(p =>
20:                       Components.ContainsKey(p.ParameterType)))//构造函数的参数类型都必须已经被DI容器所注册
21:           .FirstOrDefault();
22:  
23:       //解析构造函数的参数对象
24:       var args = ctor
25:           .GetParameters()
26:           .Select(p => Resolve(p.ParameterType))
27:           .ToArray();
28:       
29:       //创建对象
30:       var o = ctor.Invoke(args);//构造函数参数注入
31:       //如果组件是单利的,那么就保存到单利容器中
32:       if (t.IsSingleton)
33:           Instances.Add(serviceType, o);
34:  
35:       //属性注入
36:       o.GetType()
37:           .GetProperties()
38:           .Where(p => Components.ContainsKey(p.PropertyType))
39:           .ForEach(p => p.SetValue(o, Resolve(p.PropertyType), null));
40:  
41:       //字段注入 
42:       o.GetType()
43:           .GetFields()
44:           .Where(p => Components.ContainsKey(p.FieldType))
45:           .ForEach(p => p.SetValue(o, Resolve(p.FieldType)));
46:  
47:       return o;
48:   }
 

五、补充集合的两个扩展方法

01:  internal static void ForEach<T>(this IEnumerable<T> coll, Action<T> handler)
02:         {
03:             foreach (var item in coll)
04:                 handler(item);
05:         }
06:  
07:         internal static bool TrueForAll<T>(this IEnumerable<T> coll, Func<T, bool> handler)
08:         {
09:             foreach (var item in coll)
10:                 if (!handler(item))
11:                     return false;
12:             return true;
13:         }

 

总结:到此完成了DI容器的核心代码,对象的创建、字段的注入、属性的注入都是通过反射来完成的,性能有待优化,另外该容器的扩展性不足,也不支持GetAll等方法,但是做为研究和学习DI容器内部的构造原理是可以的,如果在项目中使用需要在健壮性等上面再稍加完善。

posted @ 2012-07-13 11:13  风云  阅读(875)  评论(1编辑  收藏  举报