第一次写博客,经验不足,有很多地方考虑不到,还请大家海涵。
一. 委托的应用场景?
1. 把方法当作参数传给其他方法时,需要使用委托。
二. 什么是委托?
1. 委托是一种类型,属于引用类型,定义委托的关键字是 delegate ,在命名空间下以及类的内部都可以定义委托。
2. 声明委托的语法:
a. [访问修饰符] delegate 返回值类型 委托类型名 ([参数])
3. 一个简单的委托应用:
class Program { //声明一个委托 delegate void DoSomething(); static void Main(string[] args) { //创建一个委托实例,并将符合委托签名的方法赋给它 DoSomething onEvening = LearningEnglish; //调用需要使用到委托的方法,并将委托实例传给对应的参数 DoSomethingBeforeSleep("Chris", onEvening); Console.ReadKey(); } //定义一个符合委托类型DoSomething签名的方法 static void LearningEnglish() { Console.WriteLine("背会单词吧。"); } //需要用到委托的方法 static void DoSomethingBeforeSleep(string name, DoSomething doSomething) { Console.WriteLine("睡前做点什么呢?"); doSomething.Invoke(); //等价于 doSomething(); Console.WriteLine($"{name}:我要睡觉了。"); } }
4. 委托的多种呈现方式:
static void Main(string[] args) { #region 常规用法: //创建一个委托实例,并将符合委托签名的方法赋给它 DoSomething onEvening = LearningEnglish; //调用需要使用到委托的方法,并将委托实例传给对应的参数 DoSomethingBeforeSleep("Chris", onEvening); #endregion #region 使用匿名方法 DoSomethingBeforeSleep("Chris",delegate { Console.WriteLine("背会单词吧。"); }); #endregion #region 使用Lambda语句 DoSomethingBeforeSleep("Chris", () => { Console.WriteLine("背会单词吧。"); } ); #endregion #region 使用Lambda表达式 DoSomethingBeforeSleep("Chris", () => Console.WriteLine("背会单词吧。")); #endregion Console.ReadKey(); }
5. 通用委托(泛型委托):System.Action 、System.Func、System.Predicate(略)
1.为了减少自定义委托类型的必要,.NET3.5(C#3.0)包含了一组通用的委托 。
1.1 Action :代表无参数且没有返回值的方法。
class Program { static void Main(string[] args) { DoSomethingBeforeSleep("Chris", LearningEnglish); Console.ReadKey(); } //定义一个符合委托类型 delegate void DoSomething() 签名的方法 static void LearningEnglish() { Console.WriteLine("背会单词吧。"); } //需要用到委托的方法 static void DoSomethingBeforeSleep(string name, Action doSomething) //Action 等价于 delegate void DoSomething() 这个类型的一个委托。 { Console.WriteLine("睡前做点什么呢?"); doSomething.Invoke(); Console.WriteLine($"{name}:我要睡觉了。"); }
1.2 Action<>:代表包含请求参数且没有返回值的方法,<>里定义参数的类型和个数。
class Program { static void Main(string[] args) { //调用包含委托的方法 DoSomethingBeforeSleep("Chris", Learnning); Console.ReadKey(); } //定义一个符合委托类型 delegate void DoSomething(string parameterName) 签名的方法 static void Learnning(string subjectName) { Console.WriteLine($"学会儿{subjectName}吧。"); } //需要用到委托的方法 static void DoSomethingBeforeSleep(string name, Action<string> doSomething) //Action 等价于 delegate void DoSomething(string parameterName) 这个类型的一个委托。 { Console.WriteLine("睡前做点什么呢?"); doSomething.Invoke("英语"); //调用这个委托的实例,并传递一个string类型的参数 Console.WriteLine($"{name}:我要睡觉了。"); }
1.3 Func<> :代表有返回值的方法,且<>中最后一个参数始终为返回值类型,之前的参数都为请求参数的类型。
class Program { static void Main(string[] args) { int times = 0; do { DoSomething("Chris", GetNowHour); times++; } while (times<10); } public static int GetNowHour() { return new Random().Next(0, 12); } public static void DoSomething(string name ,Func<int> whatTime) //Func<int> 等价于委托 delegate int DoSomething(); { var hour = whatTime.Invoke(); switch (hour) { case 5: Console.WriteLine($"{name}该起床"); break; case 7: Console.WriteLine($"{name}出门"); break; case 12: Console.WriteLine($"{name}吃午饭"); break; default: Console.WriteLine($"{name}不知道该干什么了。"); break; } }
6.多播委托 :一个含有多个方法的委托
6.1:不要依赖于为委托增加方法时的顺序,虽然有时看起来一致。
6.2 : 多播委托中只能存储同一类型的委托.
class Program { //声明一个委托 internal delegate void DoSomething(); static void Main(string[] args) { //创建一个委托实例,并将符合委托签名的方法赋给它 DoSomething onEvening = LearningEnglish; //多播委托:为一个委托实例赋予多个方法 onEvening += WatchTV; onEvening += PlayGames; //调用需要使用到委托的方法,并将委托实例传给对应的参数 DoSomethingBeforeSleep("Chris", onEvening); Console.ReadKey(); } //定义一个符合委托类型DoSomething签名的方法 static void LearningEnglish() { Console.WriteLine("背会单词吧。"); } static void WatchTV() { Console.WriteLine("看会电视吧。"); } static void PlayGames() { Console.WriteLine("玩会游戏吧。"); } //需要用到委托的方法 static void DoSomethingBeforeSleep(string name, DoSomething doSomething) { Console.WriteLine("睡前做点什么呢?"); doSomething.Invoke(); //等价于 doSomething(); Console.WriteLine($"{name}:我要睡觉了。"); }
6.3 可以通过调用GetInvocationList()方法, 返回当前委托中的所有方法的返回值.
Delegate [] delegate = onEvening.GetInvocationList();
6.4 多次为同一个委托对象赋值时,后者会覆盖前者。
DoSomething onEvening = LearningEnglish; onEvening = WatchTV; //覆盖LearningEnglish 方法 onEvening = PlayGames; //覆盖 WatchTV 方法
6.5 多播委托中,如果其中某个方法执行时发生异常,则后续的方法不再执行.
class Program { //声明一个委托 internal delegate void DoSomething(); static void Main(string[] args) { //创建一个委托实例,并将符合委托签名的方法赋给它 DoSomething onEvening = LearningEnglish; onEvening = WatchTV; //覆盖LearningEnglish 方法 onEvening = PlayGames; //覆盖 WatchTV 方法 //调用需要使用到委托的方法,并将委托实例传给对应的参数 DoSomethingBeforeSleep("Chris", onEvening); Console.ReadKey(); } //定义一个符合委托类型DoSomething签名的方法 static void LearningEnglish() { Console.WriteLine("背会单词吧。"); } static void WatchTV() { Console.WriteLine("看会电视吧。"); throw new Exception("这里出了一个BUG"); } static void PlayGames() { Console.WriteLine("玩会游戏吧。"); } //需要用到委托的方法 static void DoSomethingBeforeSleep(string name, DoSomething doSomething) { Console.WriteLine("睡前做点什么呢?"); doSomething.Invoke(); //等价于 doSomething(); Console.WriteLine($"{name}:我要睡觉了。"); } }
7. 委托中的协变、逆变、闭包
7.1 闭包:通过Lambda表达式访问Lambda表达式块外的变量,这称之为闭包,闭包是一个非常好的功能,但如果使用不当,也会非常危险。(引自 C#高级编程 8.3.3)
static void Main(string[] args) { int someVal = 5; Func<int, int> f = x => x + someVal; Console.WriteLine(f(3)); someVal = 7; Console.WriteLine(f(3)); Console.ReadKey(); }
本篇博客参考:《C#高级编程》、《C#本质论》、《MDSN》、http://www.cnblogs.com/DeepLearing/p/4594518.html