一步一步造个IoC轮子(三):构造基本的IoC容器

思考并回答以下问题:

定义容器
首先,我们来画个大饼,定义好构造函数,注册函数及获取函数这几个最基本的使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/// <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魔法要退化回双检锁了:(

当然在不使用索引的情况下我们还是可以保留一个魔法单例的_(:з」∠)_

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
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容器嘛,不就代理一下这个魔法工厂的操作,来来来,接下来折腾这容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/// <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能不能用,性能如何

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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

0%