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. MySql的索引操作

    索引是一种特殊的数据库结构,可以用来快速查询数据库表中的特定记录.索引是提高数据库性能的重要方式.MySQL中,所有的数据类型都可以被索引.MySQL的索引包括普通索引.唯一性索引.全文索引.单列索引 ...

  2. HTML 三角符号

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  3. UOJ #218. 【UNR #1】火车管理

    Description Solution 实际上添加问题就是一个线段树区间覆盖问题,打标记就好 对于弹栈操作比较难搞,实际上也就是一个历史查询,我们不需要保存栈中的每一个元素,我们通过查找历史状态就可 ...

  4. js事件绑定简单写法

    $E.on = function (o, e, f) { return o.addEventListener ? o.addEventListener(e, f, false) : o.attachE ...

  5. android 生成随机数

    /**  * 随机数.字母 工具类  * Created by admin on 2017/2/20.  */ public class RandomUntil {     /**      * 生成 ...

  6. [android] 练习使用ListView(一)

    练习使用ListView,BaseAdapter,先展示文字的,再练习图片的 MainActivity.java package com.android.test; import android.ap ...

  7. 设计模式入门,单件模式,c++代码实现

    // test05.cpp : Defines the entry point for the console application.// #include "stdafx.h" ...

  8. Cocos2d-js 开发记录:骨骼动画载入

    不得不说cocos2d-js的文档实在是少,骨骼动画的载入和C++版本的好像还有些不同不能直接依样画葫芦. 一个由cocos studio编辑生成的骨骼动画一般会包含如下几个文件: .ExportJs ...

  9. csharp:Microsoft.Ink 手写识别(HandWriting Recognition)

    /* 下載: //Microsoft Windows XP Tablet PC Edition 2005 Recognizer Pack http://www.microsoft.com/zh-cn/ ...

  10. css-css的基本选择器(三种)

    ** 要对哪个标签里面的数据进行操作 (1)标签选择器 div { background-color:red; color:blue; } (2)class选择器 * 每个HTML标签中都有一个属性 ...