淺聊IOC

1.概述

    IOC:有不少人把控制反轉和依賴注入混爲一談,雖然在某種意義上來看他們是一體的,但好像又有些不一樣。緩存

               1. IOC(控制反轉)是一個控制容器,DI(依賴注入)就是這個容器的運行機制。安全

               2. IOC就是一種軟件設計思想,DI是這種軟件設計思想的一個實現。框架

    關於Ioc的框架有不少,好比astle Windsor、Unity、Spring.NET、StructureMap,咱們這邊使用微軟提供的Unity作示例,你可使用 Nuget 添加 Unity ,ide

也能夠引用Microsoft.Practices.Unity.dll和Microsoft.Practices.Unity.Configuration.dll。函數

2.代碼演示

    a.  先介紹常規作法,代碼比較簡單,稍微看一下:測試

    public interface IBaseRepository
    {
        void DoSomething();
    }

    public interface IRespository : IBaseRepository { }

    public interface IUserRepository : IBaseRepository { }

    public interface IShopRepository : IBaseRepository { }

    public class Repository : IRespository
    {
        public void DoSomething()
        {
            Console.WriteLine("I am a Repository");
        }
    }

    public class UserRepository : IUserRepository
    {
        public void DoSomething()
        {
            Console.WriteLine("I am a UserRepository");
        }
    }

    public class ShopRepository : IShopRepository
    {
        public void DoSomething()
        {
            Console.WriteLine("I am a ShopRepository");
        }
    }

    public interface ITestService
    {
        void DoSomething();
    }

    public class TestService : ITestService
    {
        private IRespository respository;

        /// <summary>
        /// 構造注入
        /// </summary>
        /// <param name="_respository"></param>
        public TestService(IRespository _respository)
        {
            this.respository = _respository;
        }

        /// <summary>
        /// 屬性注入
        /// </summary>
        [Dependency]
        public IUserRepository UserRepository { get; set; }

        public IShopRepository ShopRepository { get; set; }

        /// <summary>
        /// 方法注入
        /// </summary>
        /// <param name="_shopRepository"></param>
        [InjectionMethod]
        public void SetShopRepository(IShopRepository _shopRepository)
        {
            this.ShopRepository = _shopRepository;
        }

        public void DoSomething()
        {
            respository.DoSomething();
            UserRepository.DoSomething();
            ShopRepository.DoSomething();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            UnityContainer container = new UnityContainer();
            container.RegisterType<IRespository, Repository>();
            container.RegisterType<IUserRepository, UserRepository>();
            container.RegisterType<IShopRepository, ShopRepository>();
            container.RegisterType<ITestService, TestService>();
            TestService testService = container.Resolve<ITestService>() as TestService;
            testService.DoSomething();
        }
    }
View Code

    b. 還有一種是配置文件作法this

        這個是配置文件:spa

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <!--要保證configSections爲configuration第一個節點-->
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration"/>
  </configSections>
  <unity>
    <containers>
      <!--定義了一個名稱爲defaultContainer的Unity容器-->
      <container name="defaultContainer">
        <register type="Demo.IRespository,Demo" mapTo="Demo.Respository, Demo"/>
        <register type="Demo.IUserRespository,Demo" mapTo="Demo.UserRespository, Demo"/>
        <register type="Demo.IShopRespository,Demo" mapTo="Demo.ShopRespository, Demo"/>
        <register type="Demo.ITestService,Demo" mapTo="Demo.TestService, Demo"/>
      </container>
    </containers>
  </unity>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
  </startup>
</configuration>
View Code

      對應的代碼也得改改:線程

           InitializeComponent();
            //建立容器
            UnityContainer container = new UnityContainer();
            UnityConfigurationSection configuration = ConfigurationManager.GetSection(UnityConfigurationSection.SectionName) as UnityConfigurationSection;
            configuration.Configure(container, "defaultContainer");
            //經過Resolve<ITestService>方法返回的是一個類型爲TestService的對象,該對象的三個屬性被進行了有效的初始化。
            //這個簡單的程序分別體現了接口注入(經過相應的接口根據配置解析出相應的實現類型)、構造器注入(屬性IRespository)、屬性注入(屬性IUserRepository)和方法注入(屬性IShopRepository)
            TestService t = container.Resolve<ITestService>() as TestService ;
            if (null != t)
            {
                 t.DoSomething();
            }  

3. Unity的生命週期

      Unity有三種注入方式,構造,屬性和方法注入,Unity注入也有本身的生命週期(默認瞬時生命週期:每次都是構造一個新的),下面就介紹下生命週期。設計

IUnityContainer container = new UnityContainer();
//默認瞬時生命週期,每次都是構造一個新的
container.RegisterType<IA, A>(new TransientLifetimeManager());

//每線程生命週期管理器,就是保證每一個線程返回同一實例
container.RegisterType<IA, A>(new PerThreadLifetimeManager());

//容器控制生命週期管理,這個生命週期管理器是RegisterInstance默認使用的生命週期管理器,也就是單件實例,UnityContainer會維護一個對象實例的強引用,每次調用的時候都會返回同一對象
container.RegisterType<IA, A>(new ContainerControlledLifetimeManager());

//分層生命週期管理器,這個管理器相似於ContainerControlledLifetimeManager,也是由UnityContainer來管理,也就是單件實例,針對某個層單例
//不過與ContainerControlledLifetimeManager不一樣的是,這個生命週期管理器是分層的,由於Unity的容器時能夠嵌套的,因此這個生命週期管理器就是針對這種狀況,當使用了這種生命週期管理器,父容器和子容器所維護的對象的生命週期是由各自的容器來管理
container.RegisterType<IA, A>(new HierarchicalLifetimeManager());

//這個生命週期是爲了解決循環引用而重複引用的生命週期
container.RegisterType<IA, A>(new PerResolveLifetimeManager());

//外部控制生命週期管理器,這個生命週期管理容許你使用RegisterType和RegisterInstance來註冊對象之間的關係,可是其只會對對象保留一個弱引用,其生命週期交由外部控制,也就是意味着你能夠將這個對象緩存或者銷燬而不用在乎UnityContainer,而當其餘地方沒有強引用這個對象時,其會被GC給銷燬掉。
//在默認狀況下,使用這個生命週期管理器,每次調用Resolve都會返回同一對象(單件實例),若是被GC回收後再次調用Resolve方法將會從新建立新的對象
container.RegisterType<IA, A>(new ExternallyControlledLifetimeManager());

 

4. 本身手動實現簡單IOC容器

     怎麼本身實現一個簡單的IOC容器呢?本次只實現一個簡單的版本,生命週期只實現了三種,分別是瞬時,單例和線程單例。

     a.首先定義一個生命週期的枚舉 :

public enum LifeTimeType
    {
        /// <summary>
        /// 瞬時
        /// </summary>
        Transient,

        /// <summary>
        /// 單例
        /// </summary>
        Singleton,

        /// <summary>
        /// 線程單例
        /// </summary>
        PerThread
    }
View Code

 

     b. 定義一個保存註冊映射信息的對象:

public class RegisterInfo
    {
        /// <summary>
        /// 目標類型
        /// </summary>
        public Type TargetType { get; set; }
        /// <summary>
        /// 生命週期
        /// </summary>
        public LifeTimeType LifeTime { get; set; }
    }
View Code

 

     c. 定義三個Attribute,直接寫一塊兒了:

 [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
    public class DependencyAttribute : Attribute
    {
        public LifeTimeType _lifeTimeType { get; set; }

        public DependencyAttribute(LifeTimeType lifeTimeType = LifeTimeType.Transient)
        {
            this._lifeTimeType = lifeTimeType;
        }
    }


 [AttributeUsage(AttributeTargets.Constructor)]
    public class InjectionConstructorAttribute : Attribute
    {
        public InjectionConstructorAttribute()
        { }
    }

    [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
    public class InjectionMethodAttribute : Attribute
    {
        private LifeTimeType _lifeTimeType { get; set; }

        public InjectionMethodAttribute(LifeTimeType lifeTimeType = LifeTimeType.Transient)
        {
            this._lifeTimeType = lifeTimeType;
        }
    }
View Code

 

     d. 定義一個容器的接口:

    public interface IIOCContainer
    {
        void RegisterType<TFrom, TTo>(LifeTimeType lifeTimeType = LifeTimeType.Transient);

        T Resolve<T>();
    }
View Code

 

     e. 實現該接口的方法,這是整個容器的關鍵代碼,代碼比較長,寫的可能有錯誤,歡迎指正:

public class IOCContainer : IIOCContainer
    {
        /// <summary>
        /// 緩存注入的配置
        /// </summary>
        private Dictionary<string, RegisterInfo> ContainerDictionary = new Dictionary<string, RegisterInfo>();

        /// <summary>
        /// 緩存起來,類型的對象實例
        /// </summary>
        private Dictionary<Type, object> TypeObjectDictionary = new Dictionary<Type, object>();

        public void RegisterType<TFrom, TTo>(LifeTimeType lifeTimeType = LifeTimeType.Transient)
        {
            ContainerDictionary.Add(typeof(TFrom).FullName, new RegisterInfo()
            {
                TargetType = typeof(TTo),
                LifeTime = lifeTimeType
            });
        }

        public T Resolve<T>()
        {
            RegisterInfo info = ContainerDictionary[typeof(T).FullName];
            Type type = ContainerDictionary[typeof(T).FullName].TargetType;
            T result = default(T);
            result = (T)CreateTypeByRegisterType(info, type);
            return result;
        }

        private object CreateObject(Type type)
        {
            ConstructorInfo[] ctorArray = type.GetConstructors();
            ConstructorInfo ctor = null;
            if (ctorArray.Count(c => c.IsDefined(typeof(InjectionConstructorAttribute), true)) > 0)
            {
                ctor = ctorArray.FirstOrDefault(c => c.IsDefined(typeof(InjectionConstructorAttribute), true));
            }
            else
            {
                ctor = ctorArray.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault();
            }
            List<object> paraList = new List<object>();
            foreach (var parameter in ctor.GetParameters())
            {
                Type paraType = parameter.ParameterType;
                RegisterInfo info = ContainerDictionary[paraType.FullName];
                Type targetType = info.TargetType;
                object para = null;
                para = CreateTypeByRegisterType(info, targetType);
                //遞歸:隱形的跳出條件,就是GetParameters結果爲空,targetType擁有無參數構造函數
                paraList.Add(para);
            }
            object objType = Activator.CreateInstance(type, paraList.ToArray());
            //屬性注入
            var properties = type.GetProperties()
                                 .Where(p => p.IsDefined(typeof(DependencyAttribute), false)).ToList();
            foreach (var item in properties)
            {
                var customAttributes =
                    item.GetCustomAttributes(typeof(DependencyAttribute), false) as DependencyAttribute[];
                if (customAttributes != null)
                {
                    Type t = item.PropertyType;
                    RegisterInfo info = ContainerDictionary[t.FullName];
                    info.LifeTime = customAttributes.FirstOrDefault()._lifeTimeType;
                    var value = CreateObject(info.TargetType);
                    item.SetValue(objType, value);
                }
            }
            //字段注入
            var filds = type.GetFields().Where(f => f.IsDefined(typeof(DependencyAttribute), false)).ToList();
            foreach (var fild in filds)
            {
                var attribute = fild.GetCustomAttribute(typeof(DependencyAttribute)) as DependencyAttribute;
                if (attribute != null)
                {
                    Type t = fild.DeclaringType;
                    RegisterInfo info = ContainerDictionary[t.FullName];
                    info.LifeTime = attribute._lifeTimeType;
                    var value = CreateObject(info.TargetType);
                    fild.SetValue(objType, value);
                }
            }

            //方法注入
            var methods = type.GetMethods().Where(m => m.IsDefined(typeof(InjectionMethodAttribute), false)).ToList();
            List<object> paramrterList = new List<object>();
            foreach (var item in methods)
            {
                var attribute = item.GetCustomAttribute(typeof(InjectionMethodAttribute)) as InjectionMethodAttribute;
                if (attribute != null)
                {
                    var parameters = item.GetParameters();
                    foreach (var parameter in parameters)
                    {
                        var paraType = parameter.ParameterType;
                        RegisterInfo info = ContainerDictionary[paraType.FullName];
                        Type targetType = info.TargetType;
                        object para = CreateTypeByRegisterType(info, targetType);
                        //遞歸:隱形的跳出條件,就是GetParameters結果爲空,targetType擁有無參數構造函數
                        paramrterList.Add(para);
                    }
                    item.Invoke(objType, paramrterList.ToArray());
                }
            }
            return objType;
        }

        private static readonly object obj = new object();

        /// <summary>
        /// 根據注入類別建立對象
        /// </summary>
        /// <param name="info"></param>
        /// <param name="targetType"></param>
        /// <returns></returns>
        private object CreateTypeByRegisterType(RegisterInfo info, Type targetType)
        {
            object para = null;
            switch (info.LifeTime)
            {
                case LifeTimeType.Transient:
                    para = this.CreateObject(targetType);
                    break;
                case LifeTimeType.Singleton:
                    //須要線程安全 雙if+lock
                    if (para == null)
                    {
                        lock (obj)
                        {
                            if (this.TypeObjectDictionary.ContainsKey(targetType))
                            {
                                para = this.TypeObjectDictionary[targetType];
                            }
                            else
                            {
                                if (para == null)
                                {
                                    para = this.CreateObject(targetType);
                                    this.TypeObjectDictionary[targetType] = para;
                                }
                            }

                        }
                    }
                    break;
                case LifeTimeType.PerThread:
                    //線程單例:線程槽,把數據存在這裏
                    {
                        string key = targetType.FullName;
                        object oValue = CallContext.GetData(key);
                        if (oValue == null)
                        {
                            para = this.CreateObject(targetType);
                            CallContext.SetData(key, para);
                        }
                        else
                        {
                            para = oValue;
                        }
                    }
                    break;
                default:
                    throw new Exception("wrong LifeTime");
            }
            return para;
        }

    }
View Code

 

 

以上代碼通過測試,可使用。

public interface IImitateScene
    {
        void DoSomeThing();
    }

    public class ImitateScene: IImitateScene
    {
        private IUserService userService;

        public ImitateScene(IUserService _userService)
        {
            this.userService = _userService;
        }

        [Dependency(LifeTimeType.Transient)]
        public IShopService ShopService { get; set; }

        public IStoreService StoreService { get; set; }

        [InjectionMethod(LifeTimeType.Transient)]
        public void SetStoreService(IStoreService _storeService)
        {
            this.StoreService = _storeService;            
        }

        public void DoSomeThing()
        {
            this.userService.DoSomeThing();
            this.StoreService.DoSomeThing();
            this.ShopService.DoSomeThing();
        }
    }
View Code

 附上項目圖,項目結構只爲了測試,未作過設計,嘿嘿

測試結果:

基本代碼就在這裏了,還有不少功能未完善,後續有時間會努力研究完善它,造造輪子。

相關文章
相關標籤/搜索