一步一步造個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 == 0) return instance0;
if (id == 1) 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 = -1;
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 = 0; i < max; i++)
{
var x = ctx.Resolve<ISMS>();
x.Send(null, 0, 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 = 0; i < max; i++)
{
var x = ctx2.Resolve<ISMS>();
x.Send(null, 0, null, null);
}
sw.Stop();
Console.WriteLine("IoC創建耗時{0}ms,平均每次{1}ns", sw.ElapsedMilliseconds, sw.ElapsedMilliseconds * 1000000M / (decimal)max);
sw.Restart();
for (var i = 0; i < max; i++)
{
var x = new XSMS();
x.Send(null, 0, 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