一步一步造个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 堆漏洞利用之 ...
随机推荐
- [Node.js] Create a model to persist data in a Node.js LoopBack API
In this lesson you will learn what a LoopBack model is, you will create a Product model using the Lo ...
- 百度消息推送SDK探究(并附上最简推送Demo)
上一篇<百度消息推送REST API探究>中了解了如何使用REST API推送消息,这一篇我们来看一下百度消息推送为我们提供的SDK. 帮助文档:http://developer.baid ...
- Oracle 自己主动内存參数依赖性
图例:在该图中使用了下面參数名称缩写: MT = MEMORY_TARGET MMT = MEMORY_MAX_TARGET ST = SGA_TARGET PAT = PGA_AGGREGATE_T ...
- GANs(生成对抗网络)初步
Image Completion with Deep Learning in TensorFlow 1. 基本思路 首先定义一个简单的.常见的概率分布,将其表示为 pz,不妨将其作为 [-1, 1] ...
- ERROR sqoop.Sqoop: Got exception running Sqoop: java.lang.RuntimeException: java.lang.RuntimeException: java.sql.SQLException: The connection property 'zeroDateTimeBehavior' acceptable values are: 'CO
使用sqoop导入数据时报了如题的异常,异常的提示是 The connection property 'zeroDateTimeBehavior' acceptable values are: 'CO ...
- KindEditor4.1.10,支持粘贴图片
转载自https://blog.csdn.net/jimmy0021/article/details/73251406 我已经忘记我是不是从这个博主的那里找到的解决kindeditor粘贴图片的方法了 ...
- 如何在spring quartz类中拿到ServletContext
ContextLoader.getCurrentWebApplicationContext().getServletContext() web.xml里加个: <listener> < ...
- 在这里23种经典设计模式UML类图汇总
创建型模式 1.FACTORY-追MM少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西,虽然口味有所不同,但不管你带MM去麦当劳或肯德基,只管向服务员说"来四个鸡翅"就 ...
- CSS拾遗(一)
重新看<精通CSS(第二版)>做一些记录,方便今后巩固. 1.外边距叠加 只有普通文档流中块框的垂直外边距才会发生外边距叠加.行内框.浮动框.或绝对定位框之间的外边距不会叠加. 2.相对定 ...
- POJ3280 Cheapest Palindrome 【DP】
Cheapest Palindrome Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 6013 Accepted: 29 ...