一步一步造个IoC轮子(三):构造基本的IoC容器
一步一步造个Ioc轮子目录
一步一步造个IoC轮子(一):Ioc是什么 一步一步造个IoC轮子(二):详解泛型工厂 一步一步造个IoC轮子(三):构造基本的IoC容器定义容器
首先,我们来画个大饼,定义好构造函数,注册函数及获取函数这几个最基本的使用方法
/// <summary>
/// IoC容器
/// </summary>
public class Container
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="cfg">配置文件,默认为启动目录下"cfg.xml"</param>
public Container(string cfg = "cfg.xml")
{ }
/// <summary>
/// 注册
/// </summary>
/// <typeparam name="F">接口或父类</typeparam>
/// <typeparam name="S">继承类</typeparam>
/// <param name="name">索引名称,默认为空</param>
public void Register<F, S>(string name = null) where S : F, new() where F : class
{ }
/// <summary>
/// 注册单例
/// </summary>
/// <typeparam name="F">接口或父类</typeparam>
/// <typeparam name="S">继承类</typeparam>
/// <param name="name"></param>
/// <param name="name">索引名称,默认为空</param>
public void RegisterSingleton<F, S>(string name = null) where S : F, new() where F : class
{ }
/// <summary>
/// 注册,对象由传入的Func委托创建
/// </summary>
/// <typeparam name="T">接口或父类</typeparam>
/// <param name="func">对象创建委托</param>
/// <param name="name">索引名称,默认为空</param>
public void Register<T>(Func<T> func, string name = null) where T : class
{ }
/// <summary>
/// 注册单例,对象由传入的Func委托创建
/// </summary>
/// <typeparam name="T">接口或父类</typeparam>
/// <param name="func">对象创建委托</param>
/// <param name="name">索引名称,默认为空</param>
public void RegisterSingleton<T>(Func<T> func, string name = null) where T : class
{ }
/// <summary>
/// 获取
/// </summary>
/// <typeparam name="T">接口或父类</typeparam>
/// <returns>注册的继承类</returns>
public T Resolve<T>() where T : class
{
throw new NotImplementedException();
}
/// <summary>
/// 获取
/// </summary>
/// <typeparam name="T">接口或父类</typeparam>
/// <param name="name">索引名称</param>
/// <returns>注册的继承类</returns>
public T Resolve<T>(string name) where T : class
{
throw new NotImplementedException();
}
/// <summary>
/// 取出当前所有注册的列表
/// </summary>
/// <typeparam name="T">接口或父类</typeparam>
/// <returns>索引名称列表,null表示无索引注册</returns>
public IList<string> GetRegisterList<T>() where T : class
{
throw new NotImplementedException();
}
}
接下来我们把上一篇魔改过的泛型工厂再魔改一下,我们把这个工厂去掉static再添加支持泛型委托创建对象的注册方法,由于整个Ioc设计不是静态使用的,所以工厂里的内部类static readonly魔法要退化回双检锁了:(
当然在不使用索引的情况下我们还是可以保留一个魔法单例的_(:з」∠)_
internal class Factory<T> where T : class
{
#region 空间换性能
private static readonly Factory<T> instance0 = new Factory<T>();
private static readonly Factory<T> instance1 = new Factory<T>();
private static readonly ConcurrentDictionary<int, Factory<T>> instances = new ConcurrentDictionary<int, Factory<T>>();
private static Func<int, Factory<T>> newFunc = (cid) => { return new Factory<T>(); };
public static Factory<T> GetFactory(int id)
{
if (id == ) return instance0;
if (id == ) return instance1;
return instances.GetOrAdd(id, newFunc);
}
#endregion #region Creaters
interface ICreater
{
T Create();
}
class Creater<U> : ICreater where U : T, new()
{
public T Create()
{
return new U();
}
}
class FuncCreater : ICreater
{
Func<T> func;
public FuncCreater(Func<T> func)
{
this.func = func;
}
public T Create()
{
return func();
}
}
class MagicSingletonCreater<U> : ICreater where U : T, new()
{
class InstanceClass
{
public static readonly T Instance = new U();
}
public T Create()
{
return InstanceClass.Instance;
}
}
class SingletonCreater<U> : ICreater where U : T, new()
{
//由于整个IoC容器不是静态的,所以不能用内部类static readonly魔法来搞,否则可能会出现多个索引名称注册了单例子,但引用了同一个对象,多个索引名称变成了别名的情况,只能用双检锁了
object locker = new object();
T instance;
public T Create()
{
if (instance == null)
{
lock (locker)
{
if (instance == null)
{
Interlocked.Exchange(ref instance, new U());
}
}
}
return instance;
}
}
class FuncSingletonCreater : ICreater
{
Func<T> func;
public FuncSingletonCreater(Func<T> func)
{
this.func = func;
}
//由于整个IoC容器不是静态的,所以不能用内部类static readonly魔法来搞,否则可能会出现多个索引名称注册了单例子,但引用了同一个对象,多个索引名称变成了别名的情况,只能用双检锁了
private object locker = new object();
private T instance;
public T Create()
{
if (instance == null)
{
lock (locker)
{
if (instance == null)
{
Interlocked.Exchange(ref instance, func());
}
}
}
return instance;
}
}
class MagicFuncSingletonCreater<S> : ICreater where S : T
{
static Func<S> magicFunc;
public MagicFuncSingletonCreater(Func<S> func)
{
magicFunc = func;
}
class InstanceClass
{
public static readonly S Instance = magicFunc();
}
public T Create()
{
return InstanceClass.Instance;
}
}
#endregion ConcurrentBag<string> regs = new ConcurrentBag<string>();
public IList<string> GetRegisterList()
{
return regs.ToList();
}
private void AddReg(string name)
{
if (regs.Contains(name)) return;
regs.Add(name);
}
#region 无索引的
private ICreater creater;
public T Get()
{
return creater.Create();
}
public void Reg<S>() where S : T, new()
{
creater = new Creater<S>();
AddReg(null);
}
public void RegSingleton<S>() where S : T, new()
{
creater = new MagicSingletonCreater<S>();
AddReg(null);
}
public void Reg(Func<T> func)
{
creater = new FuncCreater(func);
AddReg(null);
}
public void RegSingleton<S>(Func<S> func) where S : T
{
creater = new MagicFuncSingletonCreater<S>(func);
AddReg(null);
}
#endregion #region 有索引的
private IDictionary<string, ICreater> creaters = new ConcurrentDictionary<string, ICreater>();
public T Get(string key)
{
ICreater ct;
if (creaters.TryGetValue(key, out ct))
return ct.Create();
throw new Exception("未注册");
}
public void Reg<S>(string key) where S : T, new()
{
creaters[key] = new Creater<S>();
AddReg(key);
}
public void RegSingleton<S>(string key) where S : T, new()
{
creaters[key] = new SingletonCreater<S>();
AddReg(key);
}
public void Reg(Func<T> func, string key)
{
creaters[key] = new FuncCreater(func);
AddReg(key);
}
public void RegSingleton(Func<T> func, string key)
{
creaters[key] = new FuncSingletonCreater(func);
AddReg(key);
}
#endregion
}
好了,有了魔法工厂,IoC容器嘛,不就代理一下这个魔法工厂的操作,来来来,接下来折腾这容器
/// <summary>
/// IoC容器
/// </summary>
public class Container
{
private static volatile int currCid = -;
private int cid; /// <summary>
/// 构造函数
/// </summary>
/// <param name="cfg">配置文件,默认为启动目录下"cfg.xml"</param>
public Container(string cfg = "cfg.xml")
{
cid = Interlocked.Increment(ref currCid);
}
/// <summary>
/// 注册
/// </summary>
/// <typeparam name="F">接口或父类</typeparam>
/// <typeparam name="S">继承类</typeparam>
/// <param name="name">索引名称,默认为空</param>
public void Register<F, S>(string name = null) where S : F, new() where F : class
{
if (name == null)
Factory<F>.GetFactory(cid).Reg<S>();
else
Factory<F>.GetFactory(cid).Reg<S>(name);
}
/// <summary>
/// 注册单例
/// </summary>
/// <typeparam name="F">接口或父类</typeparam>
/// <typeparam name="S">继承类</typeparam>
/// <param name="name"></param>
/// <param name="name">索引名称,默认为空</param>
public void RegisterSingleton<F, S>(string name = null) where S : F, new() where F : class
{
if (name == null)
Factory<F>.GetFactory(cid).RegSingleton<S>();
else
Factory<F>.GetFactory(cid).RegSingleton<S>(name);
}
/// <summary>
/// 注册,对象由传入的Func委托创建
/// </summary>
/// <typeparam name="F">接口或父类</typeparam>
/// <param name="func">对象创建委托</param>
/// <param name="name">索引名称,默认为空</param>
public void Register<F>(Func<F> func, string name = null) where F : class
{
if (name == null)
Factory<F>.GetFactory(cid).Reg(func);
else
Factory<F>.GetFactory(cid).Reg(func, name);
}
/// <summary>
/// 注册单例,对象由传入的Func委托创建
/// </summary>
/// <typeparam name="F">接口或父类</typeparam>
/// <param name="func">对象创建委托</param>
/// <param name="name">索引名称,默认为空</param>
public void RegisterSingleton<F>(Func<F> func, string name = null) where F : class
{
if (name == null)
Factory<F>.GetFactory(cid).RegSingleton(func);
else
Factory<F>.GetFactory(cid).RegSingleton(func, name);
}
/// <summary>
/// 获取
/// </summary>
/// <typeparam name="F">接口或父类</typeparam>
/// <returns>注册的继承类</returns>
public F Resolve<F>() where F : class
{
return Factory<F>.GetFactory(cid).Get();
}
/// <summary>
/// 获取
/// </summary>
/// <typeparam name="F">接口或父类</typeparam>
/// <param name="name">索引名称</param>
/// <returns>注册的继承类</returns>
public F Resolve<F>(string name) where F : class
{
return Factory<F>.GetFactory(cid).Get(name);
}
/// <summary>
/// 取出当前所有注册的列表
/// </summary>
/// <typeparam name="F">接口或父类</typeparam>
/// <returns>索引名称列表,null表示无索引注册</returns>
public IList<string> GetRegisterList<F>() where F : class
{
return Factory<F>.GetFactory(cid).GetRegisterList();
}
}
基本的IoC容器已经完成,读取配置的方法我们下一篇再处理,先来点测试吧,看看这个魔法IoC能不能用,性能如何
public static void Main(string[] args)
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); var ctx = new Container(); ctx.RegisterSingleton<ISMS, XSMS>();
ctx.Register<ISMS, FriendSMS>("fsms"); var cs = ctx.GetRegisterList<ISMS>();
foreach (var c in cs)
{
//Console.WriteLine("ctx ISMS注册:" + c);
} Console.WriteLine("请输入循环次数");
int max = int.Parse(Console.ReadLine());
var sw = new Stopwatch();
sw.Start();
for (var i = ; i < max; i++)
{
var x = ctx.Resolve<ISMS>();
x.Send(null, , null, null);
}
sw.Stop();
Console.WriteLine("IoC单例耗时{0}ms,平均每次{1}ns", sw.ElapsedMilliseconds, sw.ElapsedMilliseconds * 1000000M / (decimal)max); var ctx2 = new Container();
ctx2.Register<ISMS, AlidayuSMS>();
ctx2.RegisterSingleton<ISMS, XSMS>("fsms"); sw.Restart();
for (var i = ; i < max; i++)
{
var x = ctx2.Resolve<ISMS>();
x.Send(null, , null, null);
}
sw.Stop();
Console.WriteLine("IoC创建耗时{0}ms,平均每次{1}ns", sw.ElapsedMilliseconds, sw.ElapsedMilliseconds * 1000000M / (decimal)max);
sw.Restart();
for (var i = ; i < max; i++)
{
var x = new XSMS();
x.Send(null, , null, null);
}
sw.Stop();
Console.WriteLine("直接创建耗时{0}ms,平均每次{1}ns", sw.ElapsedMilliseconds, sw.ElapsedMilliseconds * 1000000M / (decimal)max);
Console.ReadLine();
}
来看看试试1000万次结果吧

请输入循环次数
10000000
IoC单例耗时181ms,平均每次18.1ns
IoC创建耗时815ms,平均每次81.5ns
直接创建耗时41ms,平均每次4.1ns
VS2015 Release模式下VS直接运行的结果
改用CMD直接运行

几乎一样的速度
改为名称索引的速度如下

比无索引的慢那么一点点,字典的速度最不是盖的,到最后篇我们再看能不能用EMIT织一个类似switch的优化方案,比如参数数量在5以下用if判断,5以上改为更好的普通字典(不是Concurrent那个)
好了这个基本的IoC容器,速度嘛,真泛型魔法加持下,无与伦比,最后一篇优化再出一个静态的版本,速度只会更高:)
好了,装逼装到这里该发代码了,注册一只GitHub上传之,Noone
一步一步造个IoC轮子(三):构造基本的IoC容器的更多相关文章
- 一步一步造个IoC轮子(一):IoC是什么
一步一步造个Ioc轮子目录 一步一步造个IoC轮子(一):IoC是什么 一步一步造个IoC轮子(二):详解泛型工厂 一步一步造个IoC轮子(三):构造基本的IoC容器 前言 .net core正式版前 ...
- 一步一步造个IoC轮子(二),详解泛型工厂
一步一步造个Ioc轮子目录 一步一步造个IoC轮子(一):Ioc是什么 一步一步造个IoC轮子(二):详解泛型工厂 一步一步造个IoC轮子(三):构造基本的IoC容器 详解泛型工厂 既然我说IoC容器 ...
- 如何一步一步用DDD设计一个电商网站(四)—— 把商品卖给用户
阅读目录 前言 怎么卖 领域服务的使用 回到现实 结语 一.前言 上篇中我们讲述了“把商品卖给用户”中的商品和用户的初步设计.现在把剩余的“卖”这个动作给做了.这里提醒一下,正常情况下,我们的每一步业 ...
- (转) 一步一步学习ASP.NET 5 (四)- ASP.NET MVC 6四大特性
转发:微软MVP 卢建晖 的文章,希望对大家有帮助.原文:http://blog.csdn.net/kinfey/article/details/44459625 编者语 : 昨晚写好的文章居然csd ...
- 一步一步深入spring(2)-三种方式来实例化bean
在一步一步深入spring(1)--搭建和测试spring的开发环境中提到了一种实例化bean的方式,也是最基本的使用构造器实例化bean 1.使用构造器实例化bean:这是最简单的方式,Spring ...
- 一步一步构建手机WebApp开发——页面布局篇
继上一篇:一步一步构建手机WebApp开发——环境搭建篇过后,我相信很多朋友都想看看实战案例,这一次的教程是页面布局篇,先上图: 如上图所示,此篇教程便是教初学者如何快速布局这样的页面.废话少说,直接 ...
- 通过Dapr实现一个简单的基于.net的微服务电商系统(四)——一步一步教你如何撸Dapr之订阅发布
之前的章节我们介绍了如何通过dapr发起一个服务调用,相信看过前几章的小伙伴已经对dapr有一个基本的了解了,今天我们来聊一聊dapr的另外一个功能--订阅发布 目录:一.通过Dapr实现一个简单的基 ...
- 通过Dapr实现一个简单的基于.net的微服务电商系统(三)——一步一步教你如何撸Dapr
目录:一.通过Dapr实现一个简单的基于.net的微服务电商系统 二.通过Dapr实现一个简单的基于.net的微服务电商系统(二)--通讯框架讲解 三.通过Dapr实现一个简单的基于.net的微服务电 ...
- 一步一步学ROP之gadgets和2free篇(蒸米spark)
目录 一步一步学ROP之gadgets和2free篇(蒸米spark) 0x00序 0x01 通用 gadgets part2 0x02 利用mmap执行任意shellcode 0x03 堆漏洞利用之 ...
随机推荐
- Codeforces Round #315 (Div. 2)——C. Primes or Palindromes?
这道题居然是一个大暴力... 题意: π(n):小于等于n的数中素数的个数 rub(n) :小于等于n的数中属于回文数的个数 然后给你两个数p,q,当中A=p/q. 然后要你找到对于给定的A.找到使得 ...
- js如何实现动态点击改变单元格颜色?
js如何实现动态点击改变单元格颜色? 一.总结 1.通过table的rows属性,遍历表格所有行,然后通过cells属性,遍历每一行中的单元格. 2.遍历的过程中,动态的为每一个单元格定义单击事件,改 ...
- 【转】priority_queue的用法
http://www.cnblogs.com/flyoung2008/articles/2136485.html priority_queue调用 STL里面的 make_heap(), pop_he ...
- 百度消息推送REST API探究
一.百度云推送介绍 云推送(Push)是百度开放云向开发者提供的消息推送服务:通过利用云端与客户端之间建立稳定.可靠的长连接来为开发者提供向客户端应用推送实时消息服务. 百度云推送服务支持推送三种类型 ...
- [Angular] @ViewChild and template #refs to get Element Ref
We can use @ViewChild with component: @ViewChild(AuthMessageComponent) message: AuthMessageComponent ...
- 学习鸟哥的Linux私房菜笔记(6)——过滤器、输入输出及管道
一.过滤器 Linux中的应用工具分为三种: 交互工具 过滤器 编辑器 能够接受数据,过滤再输出的工具,称之为过滤器 对过滤器和进程,存在着输入源与输出对象 二.输入.输出.重定向 输入:过滤器的数据 ...
- NOIP模拟 path - 按二进制位分组
题目原文: 企鹅豆豆即将要去考长跑了,但是作为一只企鹅,长跑自然比不过鸵鸟和鸡.为了公平起见,教练告诉豆豆,他可以从 K 个指定地点中选择两个不同的地点分别作为起点和终点来考试.考试地图是一个由 N ...
- sql数据库时间转换convert
CONVERT CONVERT将某种数据类型的表达式显式转换为另一种数据类型. 严格来说,CONVERT不属于日期处理函数,只是它被经常用于日期处理中,所以这里把它列入了其他日期处理函数,下面是CON ...
- 【codeforces 777C】 Alyona and Spreadsheet
[题目链接]:http://codeforces.com/contest/777/problem/C [题意] 给你n行m列的矩阵: 然后给你k个询问[l,r]; 问你在第l到第r行,是否存在一个列, ...
- gdal库中设置prj4库全路径的用法
作者:朱金灿 来源:http://blog.csdn.net/clever101 gdal库实现投影转换之类的功能实际上底层都是调用prj4库的功能.如果gdal使用非静态的方式集成prj4库,实际上 ...