首页 > 代码库 > 几句代码简单实现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容器