一步一步造个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容器的更多相关文章

  1. 一步一步造个IoC轮子(一):IoC是什么

    一步一步造个Ioc轮子目录 一步一步造个IoC轮子(一):IoC是什么 一步一步造个IoC轮子(二):详解泛型工厂 一步一步造个IoC轮子(三):构造基本的IoC容器 前言 .net core正式版前 ...

  2. 一步一步造个IoC轮子(二),详解泛型工厂

    一步一步造个Ioc轮子目录 一步一步造个IoC轮子(一):Ioc是什么 一步一步造个IoC轮子(二):详解泛型工厂 一步一步造个IoC轮子(三):构造基本的IoC容器 详解泛型工厂 既然我说IoC容器 ...

  3. 如何一步一步用DDD设计一个电商网站(四)—— 把商品卖给用户

    阅读目录 前言 怎么卖 领域服务的使用 回到现实 结语 一.前言 上篇中我们讲述了“把商品卖给用户”中的商品和用户的初步设计.现在把剩余的“卖”这个动作给做了.这里提醒一下,正常情况下,我们的每一步业 ...

  4. (转) 一步一步学习ASP.NET 5 (四)- ASP.NET MVC 6四大特性

    转发:微软MVP 卢建晖 的文章,希望对大家有帮助.原文:http://blog.csdn.net/kinfey/article/details/44459625 编者语 : 昨晚写好的文章居然csd ...

  5. 一步一步深入spring(2)-三种方式来实例化bean

    在一步一步深入spring(1)--搭建和测试spring的开发环境中提到了一种实例化bean的方式,也是最基本的使用构造器实例化bean 1.使用构造器实例化bean:这是最简单的方式,Spring ...

  6. 一步一步构建手机WebApp开发——页面布局篇

    继上一篇:一步一步构建手机WebApp开发——环境搭建篇过后,我相信很多朋友都想看看实战案例,这一次的教程是页面布局篇,先上图: 如上图所示,此篇教程便是教初学者如何快速布局这样的页面.废话少说,直接 ...

  7. 通过Dapr实现一个简单的基于.net的微服务电商系统(四)——一步一步教你如何撸Dapr之订阅发布

    之前的章节我们介绍了如何通过dapr发起一个服务调用,相信看过前几章的小伙伴已经对dapr有一个基本的了解了,今天我们来聊一聊dapr的另外一个功能--订阅发布 目录:一.通过Dapr实现一个简单的基 ...

  8. 通过Dapr实现一个简单的基于.net的微服务电商系统(三)——一步一步教你如何撸Dapr

    目录:一.通过Dapr实现一个简单的基于.net的微服务电商系统 二.通过Dapr实现一个简单的基于.net的微服务电商系统(二)--通讯框架讲解 三.通过Dapr实现一个简单的基于.net的微服务电 ...

  9. 一步一步学ROP之gadgets和2free篇(蒸米spark)

    目录 一步一步学ROP之gadgets和2free篇(蒸米spark) 0x00序 0x01 通用 gadgets part2 0x02 利用mmap执行任意shellcode 0x03 堆漏洞利用之 ...

随机推荐

  1. 打开cad文件的几种方法

    转自原文 打开cad文件的几种方法 IWorkspaceFactory pWorkspaceFactory; IFeatureWorkspace pFeatureWorkspace; IFeature ...

  2. HOOK API入门之Hook自己程序的MessageBoxW(简单入门)

    说到HOOK,我看了很多的资料和教程,无奈就是学不会HOOK,不懂是我的理解能力差,还是你们说的 不够明白,直到我看了以下这篇文章,终于学会了HOOK: http://blog.sina.com.cn ...

  3. VMWare提供三种工作模式桥接(bridge)、NAT(网络地址转换)和host-only(主机模式)

    1.桥接模式 在桥接模式下,VMWare虚拟出来的操作系统就像是局域网中的一台独立的主机(主机和虚拟机处于对等地位),它可以访问网内任何一台机器.在桥接模式下,我们往往需要为虚拟主机配置IP地址.子网 ...

  4. C#基础readonly 与const

    readonly 与 const readonly是运行时常量,const是编译期常量(在编译过程中已经把使用该值的都用值替代,不分配内存)readonly灵活性高,const效率高 readonly ...

  5. 【u204】高级砝码称重

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 现有n个砝码,重量分别为a1,a2,a3,--,an,在去掉m个砝码后,问最多能称量出多少不同的重量( ...

  6. AJAX代码格式

    var request; //XMLHttpRequest的创建 function createRequest(url){ if(window.XMLHttpRequest){ request = n ...

  7. 2014-07-20 体验到的不是北漂easy

    北京首出租天,房子很潮,这房子我住了一个多月,我希望我真的不会活得很长,世界上只有一个真正的租房,只有明确的家是最好的. 550每月,不包括水电费.我不知道该怎么形容,房间里闪耀的太阳.一个窗口,一扇 ...

  8. 学习鸟哥的Linux私房菜笔记(2)——基础指令

    ls :列出文件和目录 ls -l :列出文件和目录的详细信息 ls -a:列出所有的文件和目录(包括隐藏目录) cp:拷贝文件 cp 源文件目录  目标目录 cp -r :拷贝目录 mv :移动或重 ...

  9. 【u242】排名系统

    Time Limit: 1 second Memory Limit: 64 MB [问题描述] F1的影响力越来越大,因此国际汽联决定扩大规模,让更多有天赋的车手加入这一行列. 不过规模的扩大带来了一 ...

  10. [Android]TextView实现分散对齐(两端对齐)

    TextView是个特别基础的Android控件,只要有文本基本就少不了它.但是最近在项目开发的过程中我发现TextView存在很多局限性,其中最令我头疼的就是TextView文本排版方面的问题.我们 ...