如何使用C#在控制臺(tái)上動(dòng)態(tài)構(gòu)建中間件管道?

如上圖所示:我們將會(huì)在下面文章上一步一步變形實(shí)現(xiàn)出這樣的功能。
一、傻瓜式執(zhí)行演示
首先建立控制臺(tái)項(xiàng)目,創(chuàng)建Begin() FirstMiddleware() SecondMiddleware() End() 三個(gè)函數(shù)
1????????///?
2????????///?開始執(zhí)行前
3????????///?
4????????public?static?void?Begin()
5????????{
6????????????Console.WriteLine("begin?executing");
7????????}
8
9????????///?
10????????///?執(zhí)行結(jié)束
11????????///?
12????????public?static?void?End()
13????????{
14????????????Console.WriteLine("end?executed");
15????????}
16
17????????public?static?void?FirstMiddleware()
18????????{
19????????????Console.WriteLine("第一個(gè)中間件");
20????????}
21????????public?static?void?SecondMiddleware()
22????????{
23????????????Console.WriteLine("第二個(gè)中間件");
24????????}
按照如下順序執(zhí)行,并顯示結(jié)果
1????????static?void?Main(string[]?args)
2????????{
3????????????//Console.WriteLine("Hello?World!");
4????????????#region?傻瓜式演示中間件執(zhí)行方法
5????????????Begin();
6????????????FirstMiddleware();
7????????????End();
8????????????Console.WriteLine("-------------------");
9????????????Begin();
10????????????SecondMiddleware();
11????????????End();
12????????????#endregion
13????????}

二、封裝傻瓜式執(zhí)行函數(shù)
這個(gè)Begin() End() 兩個(gè)方法會(huì)執(zhí)行多次,所以我們可以選擇封裝一下
1????????///?
2????????///?封裝一個(gè)執(zhí)行方法?減少重復(fù)代碼
3????????///?入?yún)⒖梢詡鬟f一個(gè)無參數(shù)?無返回值函數(shù)?用內(nèi)置委托接受
4????????///?
5????????///?
6????????public?static?void?CommonExecute(Action?function)
7????????{
8????????????Begin();
9????????????function();
10????????????End();
11????????}
如下調(diào)用,執(zhí)行結(jié)果和前文一樣
1????????????#region?封裝傻瓜式執(zhí)行函數(shù)
2????????????CommonExecute(FirstMiddleware);
3????????????Console.WriteLine("-------------------");
4????????????CommonExecute(SecondMiddleware);
5????????????#endregion
三、利用lambda變形簡化
這里我們需要做個(gè)演變,就是將我們要執(zhí)行的CommonExecute()函數(shù)用lambda表達(dá)式包裹為另一個(gè)函數(shù)的參數(shù)傳遞;
于是創(chuàng)建一個(gè)ExtraExecute() 函數(shù)
1????????public?static?void?ExtraExecute(Action?function)
2????????{
3????????????try
4????????????{
5????????????????Console.WriteLine("try?here");
6????????????????function();
7????????????}
8????????????catch?(Exception)
9????????????{
10
11????????????????throw;
12????????????}
13????????}
我們將包裹后的CommonExecute()作為入?yún)鬟f給ExtraExecute()調(diào)用,如下所示:
1????????????ExtraExecute(()?=>?CommonExecute(FirstMiddleware));?//?執(zhí)行第一個(gè)中間件,先執(zhí)行ExtraExecute() 再執(zhí)行CommonExecute()
2????????????Console.WriteLine("-----------");
3????????????ExtraExecute(()?=>?CommonExecute(SecondMiddleware));?//?執(zhí)行第二個(gè)中間件

通過觀察發(fā)現(xiàn)CommonExecute() 與ExtraExecute()方法簽名一樣,所以我們可以調(diào)換下兩者的執(zhí)行順序,如下所示:
1????????????Console.WriteLine("-----------");
2????????????//?更改中間件調(diào)用順序?先執(zhí)行CommonExecute()?再執(zhí)行ExtraExecute()
3????????????CommonExecute(()?=>?ExtraExecute(FirstMiddleware));

帶參創(chuàng)建中間件調(diào)用鏈
我們剛剛演示了無參的中間件調(diào)用,現(xiàn)在給中間件執(zhí)行體增加入?yún)ⅲ略?strong style="font-size: inherit;color: inherit;line-height: inherit;">FirstPipe(string msg, Action將中間件執(zhí)行函數(shù)包裹成參數(shù)傳遞給下一個(gè)執(zhí)行的函數(shù)
1?Action?pipe?=?(msg)?=>?ThirdPipe(msg,
2????????????????????????????????????????????????????????(msg1)?=>?SecondPipe(msg1,
3????????????????????????????????????????????????????????????(msg2)?=>?FirstPipe(msg2,?FirstMiddleware)));
4?pipe("Hello?World");?//?調(diào)用

動(dòng)態(tài)創(chuàng)建中間件管道模型
從帶參創(chuàng)建中間件調(diào)用鏈上我們發(fā)現(xiàn),我們無法把這個(gè)管道變成動(dòng)態(tài)的,那么如何才能將中間件動(dòng)態(tài)傳入以及動(dòng)態(tài)改變調(diào)用順序呢?
通過分析我發(fā)現(xiàn),一個(gè)管道執(zhí)行的最基本單元可以像下面這樣定義
1????????public?abstract?class?DynamicPipeBase
2????????{
3????????????protected?readonly?Action<string>?_action;
4
5????????????public?DynamicPipeBase(Action<string>?action)
6????????????{
7????????????????this._action?=?action;
8????????????}
9????????????public?abstract?void?Handle(string?msg);?//?調(diào)用執(zhí)行中間件函數(shù)體
10????????}
然后定義三個(gè)函數(shù)繼承 DynamicPipeBase()
1????????///?
2????????///?第三個(gè)中間件
3????????///?
4????????public?class?ThirdSelfPipe?:?DynamicPipeBase
5????????{
6????????????public?ThirdSelfPipe(Action<string>?action):base(action)
7????????????{
8
9????????????}
10????????????public?override?void?Handle(string?msg)
11????????????{
12????????????????Console.WriteLine("Third?Pipe?Begin?executing");
13????????????????_action(msg);
14????????????????Console.WriteLine("Third?Pipe?End?executed");
15????????????}
16????????}
17
18????????public?class?SecondSelfPipe?:?DynamicPipeBase
19????????{
20????????????public?SecondSelfPipe(Action<string>?action)?:?base(action)
21????????????{
22
23????????????}
24????????????public?override?void?Handle(string?msg)
25????????????{
26????????????????Console.WriteLine("Second?Pipe?Begin?executing");
27????????????????_action(msg);
28????????????????Console.WriteLine("Second?Pipe?End?executed");
29????????????}
30????????}
31
32????????///?
33????????///?第一個(gè)中間件
34????????///?
35????????public?class?FirstSelfPipe?:?DynamicPipeBase
36????????{
37????????????public?FirstSelfPipe(Action<string>?action)?:?base(action)
38????????????{
39
40????????????}
41????????????public?override?void?Handle(string?msg)
42????????????{
43????????????????Console.WriteLine("First?Pipe?Begin?executing");
44????????????????_action(msg);
45????????????????Console.WriteLine("First?Pipe?End?executed");
46????????????}
47????????}
有了中間件,我們接著只需要構(gòu)建管道即可,如下所示
1????????///?
2????????///?存儲(chǔ)中間件:?List ?_types;
3????????///?生成中間件調(diào)用鏈:?GeneratePipe
4????????///?構(gòu)建回調(diào):Build
5????????///?
6????????public?class?PipeBuilder
7????????{
8????????????protected?readonly?Action<string>?_mainAction;
9????????????List?_types;
10????????????public?PipeBuilder(Action<string>?mainAction)
11????????????{
12????????????????_mainAction?=?mainAction;
13????????????????_types?=?new?List();
14????????????}
15????????????public?PipeBuilder?AddPipe(Type?type)
16????????????{
17????????????????_types.Add(type);
18????????????????return?this;
19????????????}
20
21????????????private?Action<string>?GeneratePipe(int?index)
22????????????{
23????????????????if(index==_types.Count-1)
24????????????????{
25????????????????????var?finalType=?(DynamicPipeBase)Activator.CreateInstance(_types[index],?_mainAction);
26????????????????????return?finalType.Handle;
27????????????????}
28????????????????else
29????????????????{
30????????????????????var?childHalde=?GeneratePipe(index?+?1);
31????????????????????return?((DynamicPipeBase)Activator.CreateInstance(_types[index],?childHalde)).Handle;
32????????????????}?
33
34????????????}
35
36????????????public?Action<string>?Build()
37????????????{
38????????????????return?GeneratePipe(0);
39????????????}
40????????}
最終我們?cè)谏隙苏{(diào)用:
1????????????#region?動(dòng)態(tài)創(chuàng)建中間件管道模型
2????????????Action<string>?pipeChain?=?new?PipeBuilder(MainAction)
3????????????????.AddPipe(typeof(ThirdSelfPipe))?//?隨意添加多個(gè)中間件執(zhí)行單元
4????????????????.AddPipe(typeof(SecondSelfPipe))
5????????????????.AddPipe(typeof(FirstSelfPipe))
6????????????????.Build();
7????????????pipeChain("Hello?World");
8????????????Console.WriteLine("-------------");
9????????????#endregion
這樣便實(shí)現(xiàn)了文章開頭展示的效果。
