浅聊IOC

时间:2023-12-11 18:13:08

1.概述

IOC:有很多人把控制反转和依赖注入混为一谈,虽然在某种意义上来看他们是一体的,但好像又有些不同。

1. IOC(控制反转)是一个控制容器,DI(依赖注入)就是这个容器的运行机制。

2. IOC就是一种软件设计思想,DI是这种软件设计思想的一个实现。

关于Ioc的框架有很多,比如astle Windsor、Unity、Spring.NET、StructureMap,我们这边使用微软提供的Unity做示例,你可以使用 Nuget 添加 Unity ,

也可以引用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();
}
}

b. 还有一种是配置文件做法

这个是配置文件:

<?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>

对应的代码也得改改:

           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
}

b. 定义一个保存注册映射信息的对象:

public class RegisterInfo
{
/// <summary>
/// 目标类型
/// </summary>
public Type TargetType { get; set; }
/// <summary>
/// 生命周期
/// </summary>
public LifeTimeType LifeTime { get; set; }
}

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;
}
}

d. 定义一个容器的接口:

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

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)) > )
{
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;
} }

以上代码经过测试,可以使用。

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();
}
}

附上项目图,项目结构只为了测试,未做过设计,嘿嘿

浅聊IOC

测试结果:

浅聊IOC

基本代码就在这里了,还有很多功能未完善,后续有时间会努力研究完善它,造造*。