委托
委托
一、什么是委托,委托的本質是什么?
- 跟方法有點類似,有參數,返回值,訪問修飾符+delegate --委托--特殊的方法?
- 委托的本質是什么?是方法嗎?
- 反編譯試試:發現定義的委托-----再CustomDelegate ----有對應的class
![]()
- 委托的本質是什么?---Class(類),繼承自一個MulticastDelegate的特殊類,自己在定義類的時候,是無法繼承的。包含的有構造函數和方法。
- 委托既然是一個類---怎么使用這個類?new ---調用方法/屬性
public class DelegateDemo
{
public delegate void NoReturnWithoutParaout();
public delegate void NoReturnWithParaout(out int x);
public delegate void NoReturnWithParaRef(ref int x);
public delegate int WithReturnWithoutPara();
public delegate int WithReturnWithPara(int x, int y);
public static void NoReturnWithoutParaoutfunc()
{
Console.WriteLine("NoReturnWithoutParaoutfunc called!");
}
public static void NoReturnWithParaoutfunc(out int x)
{
x = 100;
Console.WriteLine("NoReturnWithParaoutfunc called!");
}
public static void NoReturnWithParaReffunc(ref int x)
{
x = 100;
Console.WriteLine("NoReturnWithParaReffunc called!");
}
public static int WithReturnWithoutParafunc()
{
Console.WriteLine("WithReturnWithoutParafunc called!");
return 100;
}
public static int WithReturnWithParafunc(int x, int y)
{
Console.WriteLine("WithReturnWithParafunc called!");
return x + y;
}
}
二、委托的實例化
- ILSpy反編譯--委托的本質其實是一個類
- 委托本質是一個類,這個類的構造參數--Method方法
- 委托可以通過New來實例化,要求傳遞一個和這個委托的參數和返回值完全匹配的方法,委托有什么參數(幾個,什么類型)---完全匹配
- 委托的實例---可以直接指向和這個委托參數+返回值完全匹配的方法;--語法糖--編譯器給我們提供便捷功能new省略掉了。
- 執行委托實例的Invoke方法--去執行這個委托實例化的指向的這個方法---執行方法;
- 就可以執行這個實例內部的三個方法
- 多種實例化:new、指向一個方法、指向一個lambda表達式
NoReturnWithoutParaout noReturnWithoutParaout = new NoReturnWithoutParaout(NoReturnWithoutParaoutfunc);
noReturnWithoutParaout();
三、委托的作用和意義
現在有一個學生類
{
public int Id { get; set; }
public string Name { get; set; }
public int ClassId { get; set; }
public int Age { get; set; }
public Student() { }
public void SayHi()
{
Console.WriteLine("大家好,我是學員,我叫{0}", this.Name);
}
public void Study()
{
Console.WriteLine("學習.net高級班公開課");
}
public static void StudyAdvanced()
{
Console.WriteLine("學習.net高級班vip課");
}
}
我們可以調用一下學生中的方法
{
Student stu = new Student()
{
Id = 1,
Name = "張三",
ClassId = 1,
Age = 18
};
stu.SayHi();
}
我們現在多了一個這樣的使用場景的問題
場景1. 學生分類?武漢人、上海人、廣東人--三種不同類型的人;問候的口語不一樣;
定義三種人:a.武漢 b.上海 c.廣東人
問好的方式有多種;
WuHan 人: 吃了么?
GuanDong 人:靚仔/靚女~ 雷猴!
BeiJing 人: 你好!
問題一:如果想要增加一個地方的人;
解決方案一:(多增加幾個方法)可以為每一個地方的人,各自定義一個方法
public void SayShangHai()
{
Console.WriteLine("招招手~");
Console.WriteLine("儂好?");
}
public void SayHiWuHan()
{
Console.WriteLine("招招手~");
Console.WriteLine("吃了么?");
}
public void SayHiGuanDong()
{
Console.WriteLine("招招手~");
Console.WriteLine("靚仔/靚女~ 雷猴!");
}
public void SayHiBeiJing()
{
Console.WriteLine("招招手~");
Console.WriteLine("你好!");
}
解決方案二:(傳遞參數來判斷)
/// 傳入參數,分辨類型----使用枚舉--避免調用方出錯;
/// 不同的類型給不同的邏輯
public enum UserType
{
Wuhan = 1,
GuangDong = 2,
BeiJing = 3
}
public void SayHi(UserType userType) //1. 哪兒的人 2 哪兒的人
{
Console.WriteLine("招招手~"); //只需要一句代碼搞定了
switch (userType)
{
case UserType.Wuhan:
Console.WriteLine("吃了么?");
break;
case UserType.BeiJing:
Console.WriteLine("你好!");
break;
case UserType.GuangDong:
Console.WriteLine("靚仔/靚女~ 雷猴!");
break;
default:
throw new Exception("No userType");
}
}
點評:
方案一:更好---每個方案,可以相互獨立,互不干擾,邏輯沒有解耦;滿足單一職責;
方案二:耦合在一起,如果有需求的升級,可能需要修改原有的方案,代碼可能會不穩定
問題二:如果我想要增加一些公共的業務邏輯呢?問好前---伴隨一個動作---“招招手”
再比較:方案二更有有優勢了; 只需要增加一句,就可以覆蓋所有的人;
方案一出現了重復代碼;
有沒有一個完美方案呢?
自上而下比較:邏輯單一,邏輯解耦,職責單一,保證代碼的穩定性;
如何寫?
思路:傳遞參數枚舉---分辨不同地方的人----給出不同的業務邏輯-----(傳枚舉usertype---選擇邏輯)----直接傳遞邏輯唄; 邏輯在哪兒? 封裝成一個方法; 其實就是需要傳遞一個方法;
問題:如何把方法當做參數來進行傳遞??? 啥??? 當然是委托;
public delegate void DoHandlerDelegate();
public void SayHiPerfect(DoHandlerDelegate doHandler)
{
Console.WriteLine("保持微笑~~");
doHandler.Invoke();
}
在主程序執行的時候
Console.WriteLine("通過委托傳遞邏輯 \r\n");
DoHandlerDelegate doHandlerWuhan = new DoHandlerDelegate(student.SayHiWuHan);
student.SayHiPerfect(doHandlerWuhan);
DoHandlerDelegate doHandlerBeiJin = new DoHandlerDelegate(student.SayHiBeiJing);
student.SayHiPerfect(doHandlerBeiJin);
DoHandlerDelegate doHandlerGuangDong = new DoHandlerDelegate(student.SayHiGuanDong);
student.SayHiPerfect(doHandlerGuangDong);
問題一:增加一個上海人 --- 增加了一個方案; 方法單一; 方法相互獨立,互不干擾;--穩定性,邏輯解耦
問題二:增加一個公共的業務邏輯,只需要在SayHiPerfect 增加一個動作即可;去掉重復代碼;
public void SayShangHai()
{
Console.WriteLine("招招手~");
Console.WriteLine("儂好?");
}
主程序中
DoHandlerDelegate doHandlerShangHi = new DoHandlerDelegate(student.SayShangHai);
student.SayHiPerfect(doHandlerShangHi);
委托的價值:
1.邏輯解耦,代碼的職責單一
2.去掉重復代碼
建議: 如果代碼中出現了嚴重的耦合----考慮使用委托來解決下
如果代碼中,出現了大量的重復代碼----考慮使用委托來解決下
四、框架內置的委托Action/Func
這里我們定義了兩個相同的委托,
public delegate void DoGodDelegate(object? o);
public delegate void ParameterizedThreadStart(object? o);
我們在單獨使用這2個委托的時候,并不會遇到什么問題,
我們可以發現無論是doRichard 還是threadStart 執行委托,都是執行的同一個方法;
兩個委托做的事兒是一模一樣;
問題
Thread thread = new Thread(threadStart);
//Thread thread1 = new Thread(doRichard); //不允許的;問題:在兩個結構,功能完全相同的委托; 有可能不能通用的; 不太合理???
Why?? 類型不一樣~~
委托的本質是一個類, 多個委托就是多個不同的類, 這些沒有繼承關系,所以不能用;
以上問題、微軟也發現了;
為了能夠讓委托能通用, 為了不用定義過多的委托;
微軟提供了兩個類功的委托: Action/Func
Action: 應對了沒有返回值的委托場景;
可有可無參數,一定沒有返回值的委托;最多可以有16個參數~~ 16個泛型版本
如果想要有17個參數的委托呢?(自己擴展)
Action<int> action1 = new Action<int>(s => { }); //接收一個int類型參數,沒有返回值
Action<int,string> action2 = new Action<int, string>((i,s) => { }); //接收一個int類型參數,沒有返回值
Action<int, string, DateTime, object, int, string, DateTime, object, int, string, DateTime, object, int, string, DateTime, object> action3 = null;
Action<int, string, DateTime, object, int, string, DateTime, object, int, string, DateTime, object, int, string, DateTime, object,int> action4 = null;
這是一個沒有返回值的通用委托---如果所有的人需要使用沒有返回值的委托的時候,都用Action, 不用定義委托;委托都是同一個類型; 就可以通用了;
微軟既然提供這個委托,自然是希望我們在以后的編碼中,都盡可能使用這些,這樣就可以統一了委托的類型;
對于以前定義的委托呢? 咋辦? 解決不了~~ 這些被稱為技術上的歷史包袱,丟不掉~~ 以后在使用委托的時候,就不要自己定義了,直接使用現有提供的委托即可~~
委托要有返回值呢?
Func:多版本的委托:應對了有返回值的情況;
一定有返回值的委托;泛型委托Func中的最后一個類型參數,是作為返回值的類型;前面的類型參數,是作為委托的參數;也提供了最多可以有16個參數;
如果需要有17個參數,且有返回值的委托呢? 可以擴展下唄;
Func<int> func = new Func<int>(() => { return 10; }); //一個返回值的委托,沒有參數;
Func<int, string> func1 = new Func<int, string>(s => { return "R"; });
Func<int,string,DateTime,object, int, string, DateTime, object, int, string, DateTime, object, int, string, DateTime, object, string> func2 = null;
//17個參數;
Func<int, string, DateTime, object, int, string, DateTime, object, int, string, DateTime, object, int, string, DateTime,object, object, string> func3 = null;
public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, in T16, in T17>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17);
public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, in T16, in T17, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17);
五、委托的嵌套使用,ASP.NET CORE中間件的核心設計----委托的多層嵌套
這里創建一個很基礎的一個類
public class DelegateExtension
{
public static void Show()
{
InvokeAction invokeAction = new InvokeAction();
{
}
}
}
public class InvokeAction {
public void ExeMethod()
{
Console.WriteLine("Exec ExeMethod");
}
}
問題:想要去在Invoke.Show方法前后,擴展一些業務邏輯.
這里根據我們之前上面拓展出來的知識可以寫出來這樣的代碼
public static void Show()
{
InvokeAction invokeAction = new InvokeAction();
{
Action action = new Action(invokeAction.ExeMethod);
//再次定義委托
{
Func<Action, Action> action2 = new Func<Action, Action>(ExeNextMethod006);
Action action3 = action2.Invoke(action);
action = action3;
Func<Action, Action> action4 = new Func<Action, Action>(ExeNextMethod005);
Action action5 = action4.Invoke(action);
action = action5;
Func<Action, Action> action6 = new Func<Action, Action>(ExeNextMethod004);
Action action7 = action6.Invoke(action);
action = action7;
Func<Action, Action> action8 = new Func<Action, Action>(ExeNextMethod003);
Action action9 = action8.Invoke(action);
action = action9;
Func<Action, Action> action10 = new Func<Action, Action>(ExeNextMethod002);
Action action11 = action10.Invoke(action);
action = action11;
Func<Action, Action> action12 = new Func<Action, Action>(ExeNextMethod001);
Action action13 = action12.Invoke(action);
action = action13;
action.Invoke();
}
}
}
public static Action ExeNextMethod001(Action action)
{
return new Action(() =>
{
Console.WriteLine("Exec ExeNextMethod001 Start");
action();
Console.WriteLine("Exec ExeNextMethod001 End");
});
}
public static Action ExeNextMethod002(Action action)
{
return new Action(() =>
{
Console.WriteLine("Exec ExeNextMethod002 Start");
action();
Console.WriteLine("Exec ExeNextMethod002 End");
});
}
public static Action ExeNextMethod003(Action action)
{
return new Action(() =>
{
Console.WriteLine("Exec ExeNextMethod003 Start");
action();
Console.WriteLine("Exec ExeNextMethod003 End");
});
}
public static Action ExeNextMethod004(Action action)
{
return new Action(() =>
{
Console.WriteLine("Exec ExeNextMethod004 Start");
action();
Console.WriteLine("Exec ExeNextMethod004 End");
});
}
public static Action ExeNextMethod005(Action action)
{
return new Action(() =>
{
Console.WriteLine("Exec ExeNextMethod005 Start");
action();
Console.WriteLine("Exec ExeNextMethod005 End");
});
}
public static Action ExeNextMethod006(Action action)
{
return new Action(() =>
{
Console.WriteLine("Exec ExeNextMethod006 Start");
action();
Console.WriteLine("Exec ExeNextMethod006 End");
});
}
}
public class InvokeAction {
public void ExeMethod()
{
Console.WriteLine("Exec ExeMethod");
}
}
我希望無限制的 增加擴展業務邏輯;
再增加一個--定義方法,通過委托包裝后去當做參數傳遞;
現在的代碼已經做到了
1.已經做到了職責的單一;
2.但是當前這個 DelegateExtension.Show 方法不太穩定,如果要增加一環,我需要修改DelegateExtension.Show方法
思路:可以把要執行的業務邏輯----特性--封裝到特性中去;
public static void Show()
{
InvokeAction invokeAction = new InvokeAction();
{
{
Type type= typeof(InvokeAction);
MethodInfo method = type.GetMethod("ExeMethod");
ExecNextMethod001Attribute attribute1 = method.GetCustomAttribute<ExecNextMethod001Attribute>();
Action action=new Action(()=> { method.Invoke(invokeAction, null); });
action=attribute1.Do(action);
ExecNextMethod002Attribute attribute2 = method.GetCustomAttribute<ExecNextMethod002Attribute>();
action = attribute2.Do(action);
ExecNextMethod003Attribute attribute3 = method.GetCustomAttribute<ExecNextMethod003Attribute>();
action = attribute3.Do(action);
ExecNextMethod004Attribute attribute4 = method.GetCustomAttribute<ExecNextMethod004Attribute>();
action = attribute4.Do(action);
action.Invoke();
}
}
}
[AttributeUsage(AttributeTargets.Method)]
public class ExecNextMethod001Attribute: Attribute
{
public Action Do(Action action)
{
return new Action(() =>
{
Console.WriteLine("Exec ExeNextMethod001 Start");
action();
Console.WriteLine("Exec ExeNextMethod001 End");
});
}
}
[AttributeUsage(AttributeTargets.Method)]
public class ExecNextMethod002Attribute : Attribute
{
public Action Do(Action action)
{
return new Action(() =>
{
Console.WriteLine("Exec ExeNextMethod002 Start");
action();
Console.WriteLine("Exec ExeNextMethod002 End");
});
}
}
[AttributeUsage(AttributeTargets.Method)]
public class ExecNextMethod003Attribute : Attribute
{
public Action Do(Action action)
{
return new Action(() =>
{
Console.WriteLine("Exec ExeNextMethod003 Start");
action();
Console.WriteLine("Exec ExeNextMethod003 End");
});
}
}
[AttributeUsage(AttributeTargets.Method)]
public class ExecNextMethod004Attribute : Attribute
{
public Action Do(Action action)
{
return new Action(() =>
{
Console.WriteLine("Exec ExeNextMethod004 Start");
action();
Console.WriteLine("Exec ExeNextMethod004 End");
});
}
}
[AttributeUsage(AttributeTargets.Method)]
public class ExecNextMethod005Attribute : Attribute
{
public Action Do(Action action)
{
return new Action(() =>
{
Console.WriteLine("Exec ExeNextMethod005 Start");
action();
Console.WriteLine("Exec ExeNextMethod005 End");
});
}
}
[AttributeUsage(AttributeTargets.Method)]
public class ExecNextMethod006Attribute : Attribute
{
public Action Do(Action action)
{
return new Action(() =>
{
Console.WriteLine("Exec ExeNextMethod006 Start");
action();
Console.WriteLine("Exec ExeNextMethod006 End");
});
}
}
思路:因為類型不一樣,所以只能一個一個的判斷;
如果能一次獲取所有特性(矛盾,類型不一樣,沒有辦法一次獲取所有;????統一類型,如何統一類型?? 來一個父類,讓特性繼承父類,然后在獲取特性的時候,通過父類獲取;):放在一個集合中,要逐個的調用特性的Do方法來包裝委托的話,就可以循環執行;
public static void Show()
{
InvokeAction invokeAction = new InvokeAction();
Type type = typeof(InvokeAction);
MethodInfo method = type.GetMethod("ExeMethod");
Action action = new Action(() => { method.Invoke(invokeAction, null); });
IEnumerable<ExecNextMethodAbstractAttribute> attributellist = method.GetCustomAttributes<ExecNextMethodAbstractAttribute>();
foreach (var attribute in attributellist.Reverse())
{
action = attribute.Do(action);
}
action.Invoke();
}
[AttributeUsage(AttributeTargets.Method)]
public abstract class ExecNextMethodAbstractAttribute : Attribute
{
public abstract Action Do(Action action);
}
[AttributeUsage(AttributeTargets.Method)]
public class ExecNextMethod001Attribute : ExecNextMethodAbstractAttribute
{
public override Action Do(Action action)
{
return new Action(() =>
{
Console.WriteLine("Exec ExeNextMethod001 Start");
action();
Console.WriteLine("Exec ExeNextMethod001 End");
});
}
}
[AttributeUsage(AttributeTargets.Method)]
public class ExecNextMethod002Attribute : ExecNextMethodAbstractAttribute
{
public override Action Do(Action action)
{
return new Action(() =>
{
Console.WriteLine("Exec ExeNextMethod002 Start");
action();
Console.WriteLine("Exec ExeNextMethod002 End");
});
}
}
[AttributeUsage(AttributeTargets.Method)]
public class ExecNextMethod003Attribute : ExecNextMethodAbstractAttribute
{
public override Action Do(Action action)
{
return new Action(() =>
{
Console.WriteLine("Exec ExeNextMethod003 Start");
action();
Console.WriteLine("Exec ExeNextMethod003 End");
});
}
}
[AttributeUsage(AttributeTargets.Method)]
public class ExecNextMethod004Attribute : ExecNextMethodAbstractAttribute
{
public override Action Do(Action action)
{
return new Action(() =>
{
Console.WriteLine("Exec ExeNextMethod004 Start");
action();
Console.WriteLine("Exec ExeNextMethod004 End");
});
}
}
[AttributeUsage(AttributeTargets.Method)]
public class ExecNextMethod005Attribute : ExecNextMethodAbstractAttribute
{
public override Action Do(Action action)
{
return new Action(() =>
{
Console.WriteLine("Exec ExeNextMethod005 Start");
action();
Console.WriteLine("Exec ExeNextMethod005 End");
});
}
}
[AttributeUsage(AttributeTargets.Method)]
public class ExecNextMethod006Attribute : ExecNextMethodAbstractAttribute
{
public override Action Do(Action action)
{
return new Action(() =>
{
Console.WriteLine("Exec ExeNextMethod006 Start");
action();
Console.WriteLine("Exec ExeNextMethod006 End");
});
}
}
這樣就可以讓我們自己在搭建框架的時候(支持了業務的擴展---AOP擴展---裝飾器模式的番外篇),可以讓程序員在使用我們這個框架的時候,更加聚焦業務邏輯;
可以動態增加業務邏輯;
六、多播委托/觀察者模式
委托還可以通過+= 把更多的方法包裝到委托中來,形成一個方法的執行鏈子
在執行委托的時候,可以按照+=的順序,把方法逐個執行;
也提供了-= 操作,可以按照順序把方法從方法鏈子中,依次移除;移除按照順序依次移除,只要是匹配移除一個之后,就不在繼續移除了;如果要移除的方法在,委托中不存在,-=就不做任何操作;
委托在+=,-=可以操作哪些方法呢?
- 靜態方法
- 實例方法
- lamdba表達式
- 普通方法
public void Show()
{
{
Action action = new Action(DoNothing);
action += DoNothing;
action += DoNothing;
action += DoNothing;
action += DoNothing;
action -= DoNothing;
action.Invoke();
}
//委托在+= 、 -=可以操作哪些方法呢?
{
Action action = new Action(DoNothing);
action += DoNothingStatic; //+=靜態方法
action += new Student().Study; //+=實例方法
action += () => { Console.WriteLine("this is Lambda"); };
//action.Invoke(); //都會依次執行
action -= DoNothing; //普通方法
action -= DoNothingStatic; //靜態方法
action -= new Student().Study; //實例方法
action -= () => { Console.WriteLine("this is Lambda"); }; //Lambda 表達式
action.Invoke();
}
}
private void DoNothing()
{
Console.WriteLine("This is DoNothing");
}
private static void DoNothingStatic()
{
Console.WriteLine("This is DoNothingStatic");
}


浙公網安備 33010602011771號