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的更多相关文章

  1. 浅聊ARP

    今天借用思科公司的Cisco Packet Tracer Student这款软件浅聊ARP 什么是ARP? ARP即地址解析协议(Address Resolution Protocol),是根据Ip地 ...

  2. 浅谈IOC

    一.引言 IOC-Invertion of Control,即控制反转,是一种程序设计思想,世上本没有路,走的人多了便有了路,本文将一步步带你了解IOC设计思想的演进之路. 在学习IOC之前我们先初步 ...

  3. 浅谈(IOC)依赖注入与控制反转(DI)

    前言:参考了百度文献和https://www.cnblogs.com/liuqifeng/p/11077592.html以及http://www.cnblogs.com/leoo2sk/archive ...

  4. require.js(浅聊)

    一.require 了解requirejs之前首先明白什么是模块化: 1.什么是模块化? 模块化设计是指在对一定范围内的不同功能或相同功能不同性能.不同规格的产品进行功能分析的基础上,划分并设计出一系 ...

  5. mysql 系统性浅聊 myisam 存储引擎【原创】

    >>思维导图 >>介绍 mysql中的存储引擎都是以插件的形式存在,目前用的最多存储引擎就是innodb和myisam.MySQL5.5.5以后(包括5.5.5)默认使用Inn ...

  6. 浅聊本人学习React的历程——第一篇生命周期篇

    作为一个前端小白,在踏入前端程序猿行业的第三年接触了React,一直对于框架有种恐惧感,可能是对陌生事物的恐惧心里吧,导致自己一直在使用原生JS和JQ作为开发首选,但是在接触了React之后,发现了其 ...

  7. Sort排序浅聊

    集合是什么?笔者简易描述:数组是不可变的,所以我们使用集合来代替. using.System.Collections; 非泛型集合 using.System.Collections.Gernerc;泛 ...

  8. 浅聊几种主流Docker网络的实现原理

    原文:https://mp.weixin.qq.com/s/Jdxct8qHrBUtkUq-hnxSRw 参考:https://blog.csdn.net/yarntime/article/detai ...

  9. 浅聊标签<include>和<viewStub>

    在开发中我们往往会遇到这种情况,当一个布局文件比较复杂时,我们一个劲地往里面拖各种控件button,textView,imageView阿等等,等过了一段时间后,出现bug,自己都把自己搞懵比啦,特别 ...

随机推荐

  1. Django 入门项目案例开发(中)

    关注微信公众号:FocusBI 查看更多文章:加QQ群:808774277 获取学习资料和一起探讨问题. 昨天已经描述了如何搭建Django的开发环境,今天描述业务流程,具体我们要实现一个什么样的业务 ...

  2. C++要点总结

    1.内联成员函数 1)隐式声明:将成员函数直接定义在类的内部 2)显式声明:inline标示 2)在类中,使用inline定义内联函数时,必须将类的声明和内联成员函数的定义都放在同一个文件中,否则编译 ...

  3. 常用工具说明--强大的PostMan

    PsotMan介绍和使用 Postman介绍 Postman是google开发的一款功能强大的网页调试与发送网页HTTP请求,并能运行测试用例的的Chrome插件.其主要功能包括: 模拟各种HTTP ...

  4. Bookstrap4 学习(一)

    容器 container 是最基本的lagyout 元素, 并且当使用默认的Grid 系统时, containers 是必须的. <div class="container" ...

  5. 流畅的python和cookbook学习笔记(一)

    1.数据结构 1.1 内置序列类型 四种序列类型: 1.容器序列:list.tuple和collections.deque 2.扁平序列:str.bytes.bytearray.memoryview和 ...

  6. js两个字符串明明一样却判断显示不相等

    一.问题 两个字符串看起来一样.类型一样,判断str1==str2时返回false: 二.原因 字符串可能含有其他特殊字符:换行符(%D).空格(%20)...一般不显示. 三.如何判断 encode ...

  7. 【转载】从创业者角度看《印度合伙人 Padman》后的一点感受

    ***************************** 这部电影看简介是真实事件改编的,当时除了电影本身的精彩和主角宠妻狂魔之外,印象最深的就是感觉到主角的创业者心态是一步步在生活中被培养的.特别 ...

  8. .NET开源工作流RoadFlow-Bug修改-1.8.2表单验证时ueditor编辑非空验证无效

    RoadFlow生成的表单,Ueditor编辑器不能进行非空验证的BUG修改: 1.修改控制器:WorkFlowFormDesignerController红框处: 2.修改js文件:Scripts/ ...

  9. Codeforces Round #415 (Div. 2) B. Summer sell-off

    B. Summer sell-off time limit per test   1 second memory limit per test   256 megabytes   Summer hol ...

  10. How to solve problems

    练习是为了帮助你成长 0.Don't panic! 1.What are the inputs? 2.What are the outputs? 3.Work through some example ...