首页 > 代码库 > 几句代码简单实现IoC容器
几句代码简单实现IoC容器
前言
最近在调试EasyNetQ代码的时候发现里面有一段代码,就是IoC容器的简单实现,跟着他的代码敲了一遍,发现了奇妙之处。当然也是因为我才疏学浅导致孤陋寡闻了。他的思路就是通过动态调用构造函数生成对象,然后将对象保存,调用的时候进行单例调用,而且,代码中不会存在 new 字眼。所有实例对象的创建和映射都在容器中实现。当然,还是用第三方的容器比较稳妥,本文中只是很简单的一个示范。具体理解的是否正确,我也不敢说,只不过,能达到一些预期的效果,功能不够强大。
解析
首先,我们先添加几个接口。IServiceProvider,IServiceRegister,IServiceContainer (继承自前两个)
/// <summary> /// 服务提供接口 /// </summary> public interface IServiceProvider { /// <summary> /// 获取某个类型的服务实例 /// </summary> /// <typeparam name="TService"> TService 接口 </typeparam> /// <returns>返回TService类型实例</returns> TService Resolve<TService>() where TService : class; }
/// <summary> /// 服务注册接口 /// </summary> public interface IServiceRegister { /// <summary> /// 注册一个服务工厂 /// </summary> /// <typeparam name="TService">工厂类</typeparam> /// <param name="serviceCreator">创建一个新的接口实例</param> /// <returns>返回当前注册工厂,以便调用其他的register方法</returns> IServiceRegister Register<TService>(Func<IServiceProvider, TService> serviceCreator) where TService : class; /// <summary> /// 注册服务类型和服务实例 /// </summary> /// <typeparam name="TService">接口实例</typeparam> /// <typeparam name="TImplementation">类型继承自TService</typeparam> /// <returns>返回当前注册工厂,以便调用其他的register方法</returns> IServiceRegister Register<TService, TImplementation>() where TService : class //TImplementation 继承自 TService where TImplementation : class, TService; }
/// <summary> /// 对外接口Container,继承自IServiceProvider,IServiceRegister /// </summary> public interface IServiceContainer : IServiceProvider, IServiceRegister { }
当然,最主要的还是 IServiceContainer 的实现,代码中有简单的注释,所以我就直接贴代码了。(看不懂的仔细认真一些就好)
/// <summary> /// 服务提供类的实现,类似服务工厂 /// </summary> public class ServiceProvider : IServiceContainer { /// <summary> /// 锁 /// </summary> private readonly object syncLock = new object(); /// <summary> /// 存储单例工厂 /// </summary> private readonly ConcurrentDictionary<Type, object> factories = new ConcurrentDictionary<Type, object>(); /// <summary> /// 存储注册类型 /// </summary> private readonly ConcurrentDictionary<Type, Type> registrations = new ConcurrentDictionary<Type, Type>(); /// <summary> /// 存储实例 /// </summary> private readonly ConcurrentDictionary<Type, object> instances = new ConcurrentDictionary<Type, object>(); #region 私有方法 /// <summary> /// 判断服务是否已经被注册过 /// </summary> /// <param name="serviceType"></param> /// <returns></returns> private bool ServiceIsRegistered(Type serviceType) { //判断是否在工厂或者注册库内已经注册过该类型 return factories.ContainsKey(serviceType) || registrations.ContainsKey(serviceType); } #endregion /// <summary> /// 注册工厂 /// </summary> /// <typeparam name="TService"></typeparam> /// <param name="serviceCreator"></param> /// <returns></returns> public IServiceRegister Register<TService>(Func<IServiceProvider, TService> serviceCreator) where TService : class { lock (syncLock) { var serviceType = typeof(TService); if (ServiceIsRegistered(serviceType)) { return this; } //将服务添加到存储器中 factories.TryAdd(serviceType, serviceCreator); return this; } } /// <summary> /// 注册实例类 /// </summary> /// <typeparam name="TService">IService 必须实现一个构造器</typeparam> /// <typeparam name="TImplementation"></typeparam> /// <returns></returns> public IServiceRegister Register<TService, TImplementation>() where TService : class where TImplementation : class, TService { lock (syncLock) { var serviceType = typeof(TService); var implType = typeof(TImplementation); if (ServiceIsRegistered(serviceType)) { return this; } //如果注册的类不是继承自TService接口,抛出异常 if (!serviceType.IsAssignableFrom(implType)) { throw new Exception(string.Format("类型 {0} 不继承接口 {1}", implType.Name, serviceType.Name)); } //获取构造方法,必须只有一个构造方法(why?) var constructors = implType.GetConstructors(); if (constructors.Length != 1) { throw new Exception(string.Format("服务实例必须有且只有一个构造方法.当前实例 {0} 拥有 {1} 个", implType.Name, constructors.Length.ToString())); } //添加 registrations.TryAdd(serviceType, implType); return this; } } /// <summary> /// 返回一个实例 /// </summary> /// <typeparam name="TService"></typeparam> /// <returns></returns> public virtual TService Resolve<TService>() where TService : class { var serviceType = typeof(TService); object service; //如果实例存储器中已经存在该实例,直接返回 if (instances.TryGetValue(serviceType, out service)) return (TService)service; lock (syncLock) { //加锁,再次判断 if (instances.TryGetValue(serviceType, out service)) { return (TService)service; } //如果注册器中存在该类型,创建该实例,并加入到实例存储器中 if (registrations.ContainsKey(serviceType)) { var implementationType = registrations[serviceType]; service = CreateServiceInstance(implementationType); instances.TryAdd(serviceType, service); } else if (factories.ContainsKey(serviceType)) { service = ((Func<IServiceProvider, TService>)factories[serviceType])(this); instances.TryAdd(serviceType, service); } else { throw new Exception(string.Format("服务类型 {0} 未注册", serviceType.Name)); } return (TService)service; } } private object Resolve(Type serviceType) { return GetType() .GetMethod("Resolve", new Type[0]) .MakeGenericMethod(serviceType) .Invoke(this, new object[0]); } private object CreateServiceInstance(Type implementationType) { //获取构造器 var constructors = implementationType.GetConstructors(); //遍历构造器中的参数类型,获取参数 var parameters = constructors[0] .GetParameters() .Select(parameterInfo => Resolve(parameterInfo.ParameterType)) .ToArray(); //创建实例方法 return constructors[0].Invoke(parameters); } }
调用方式
写个测试:IAnimal 接口中有Shout方法。在创建两个类继承自IAnimal,Dog,Cat。 IPeople中有个Pet方法,在创建两个类,Man,Women 继承自IPeople。 其中IPeople中初始化要传入IAnimal的实例,来确定养的宠物是猫还是狗.
代码如下:
public interface IAnimal { void Shout(); } public class Dog : IAnimal { public void Shout() { Console.WriteLine("汪汪汪"); } } public class Cat : IAnimal { public void Shout() { Console.WriteLine("喵喵喵"); } }
public interface IPeople { void Pet(); } public class Man : IPeople { private IAnimal _animal; public Man(IAnimal animal) { _animal = animal; } public void Pet() { Console.WriteLine("[男]我有一个宠物,它在:"); _animal.Shout(); } } public class Woman : IPeople { private IAnimal _animal; public Woman(IAnimal animal) { _animal = animal; } public void Pet() { Console.WriteLine("[女]我有一个宠物,它在:"); _animal.Shout(); } }
测试代码
static void Main(string[] args) { Func<IServiceContainer> getContainer = () => new ServiceProvider(); IServiceContainer container = getContainer(); //注册 container.Register(_ => container) //注册Dog实例 .Register<IAnimal, Dog>() //注册Woman实例 .Register<IPeople, Woman>(); //得到people var people = container.Resolve<IPeople>(); //调用pet方法 people.Pet(); Console.Read(); }
执行结果:
同样,当我们把 Dog换成Cat
我们把animal的注册去掉,由于people依赖于animal,所以会报错误。
总结
这是一个简单的IoC容器的实现,相信真正的要复杂的很多,不过看EasyNetQ就直接这么用的,我觉得如果需求不高的话,也可以用这种方法。而且,代码看起来清洁了许多,不用自己new对象,而且,内部依赖也可以轻松解决。我该去看看真正的容器是怎么实现的了,byebye
几句代码简单实现IoC容器