委托:
还记得C++里的函数指针么?大家可以点击这里查看一下以前的笔记。C#的委托和C++中的函数指针效果一致。
当我们需要将函数作为对象进行传递和使用时就需要用到委托。
下面我们看一个例子:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Test 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 //声明委托的实例 14 ProcessDelegate pd; 15 16 //指定委托的函数 17 pd = Multiply; 18 Console.WriteLine(pd(12, 6)); 19 20 //指定委托的函数 21 pd = Divide; 22 Console.WriteLine(pd(12, 6)); 23 24 //将委托作为参数传递 25 Func1(pd); 26 //将函数直接作为参数传递 27 Func1(Multiply); 28 29 Console.ReadKey(); 30 } 31 32 /// <summary> 33 /// 声明一个委托. 34 /// </summary> 35 delegate double ProcessDelegate(double param1, double param2); 36 37 static double Multiply(double param1, double param2) 38 { 39 return param1 * param2; 40 } 41 42 static double Divide(double param1, double param2) 43 { 44 return param1 / param2; 45 } 46 47 /// <summary> 48 /// 参数为委托类型的函数. 49 /// </summary> 50 static void Func1(ProcessDelegate pd) 51 { 52 Console.WriteLine(pd(55, 11)); 53 } 54 } 55 }
运行的结果如下:
1 72 2 2 3 5 4 605
初始化定义和委托推断:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Test 8 { 9 class Program 10 { 11 delegate string GetAString(); 12 13 static void Main(string[] args) 14 { 15 int x = 40; 16 //这里我称为初始化定义, 注意不能写成 x.ToString() 17 GetAString method = new GetAString(x.ToString); 18 Console.WriteLine(method()); 19 20 x = 123; 21 //通过委托推断可以简化代码编写, 注意不能写成 x.ToString() 22 GetAString method2 = x.ToString; 23 Console.WriteLine(method2()); 24 25 Console.ReadKey(); 26 } 27 } 28 }
多播委托:
多播委托支持“+”“-”操作符,可以添加多个方法到同一个委托中,当委托被执行时,并不会按照添加的顺序依次调用函数,调用顺序是无法保证的。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Test 8 { 9 class Program 10 { 11 delegate void DoubleOp(double value); 12 13 public static void Func1(double value) 14 { 15 double result = value * 2; 16 Console.WriteLine("Func1 value: {0}, result: {1}", value, result); 17 } 18 19 public static void Func2(double value) 20 { 21 double result = value * value; 22 Console.WriteLine("Func2 value: {0}, result: {1}", value, result); 23 } 24 25 static void Main(string[] args) 26 { 27 DoubleOp op = Func1; 28 //添加一个方法 29 op += Func2; 30 31 op(1); 32 Console.WriteLine(); 33 op(2); 34 Console.WriteLine(); 35 36 //去掉一个方法 37 op -= Func1; 38 39 op(3); 40 41 Console.ReadKey(); 42 } 43 } 44 }
下面是运行的结果:
1 Func1 value: 1, result: 2 2 Func2 value: 1, result: 1 3 4 Func1 value: 2, result: 4 5 Func2 value: 2, result: 4 6 7 Func2 value: 3, result: 9
匿名函数:
使用时直接进行定义的函数。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Test 8 { 9 class Program 10 { 11 delegate string GetString(float num); 12 13 static void Main(string[] args) 14 { 15 //定义匿名函数 16 GetString m = delegate(float num) 17 { 18 return num.ToString(); 19 }; 20 Func(m, 10.5f); 21 22 //直接传递匿名函数 23 Func(delegate(float num) 24 { 25 num *= 2.0f; 26 return num.ToString(); 27 }, 20.5f); 28 29 Console.ReadKey(); 30 } 31 32 static void Func(GetString method, float num) 33 { 34 Console.WriteLine("Func: " + method(num)); 35 } 36 } 37 }
下面是运行的结果:
1 Func: 10.5 2 Func: 41
Lambda表达式:
Lambda表达式可以用来简化匿名函数的写法,如果把上面的示例改为Lambda表达试则如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Test 8 { 9 class Program 10 { 11 delegate string GetString(float num); 12 13 static void Main(string[] args) 14 { 15 //使用表达式进行简写 16 GetString m = num => 17 { 18 return num.ToString(); 19 }; 20 Func(m, 10.5f); 21 22 //直接传递表达试 23 Func(num => 24 { 25 num *= 2.0f; 26 return num.ToString(); 27 }, 20.5f); 28 29 Console.ReadKey(); 30 } 31 32 static void Func(GetString method, float num) 33 { 34 Console.WriteLine("Func: " + method(num)); 35 } 36 } 37 }
Lambda表达式可以去掉函数参数的类型,因为该类型编译器可以从上下文中获得。如果存在多个参数则需要添加括号,如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Test 8 { 9 class Program 10 { 11 delegate string GetString(float num, int num2); 12 13 static void Main(string[] args) 14 { 15 //使用表达式进行简写 16 GetString m = (num, num2) => 17 { 18 return num.ToString() + "," + num2.ToString(); 19 }; 20 Func(m, 10.5f, 100); 21 22 //直接传递表达试 23 Func((num, num2) => 24 { 25 num *= 2.0f; 26 num += num2; 27 return num.ToString(); 28 }, 20.5f, 200); 29 30 Console.ReadKey(); 31 } 32 33 static void Func(GetString method, float num, int num2) 34 { 35 Console.WriteLine("Func: " + method(num, num2)); 36 } 37 } 38 }
下面是运行的结果:
1 Func: 10.5,100 2 Func: 241
如果代码仅有一行还可以省略return和大括号,如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Test 8 { 9 class Program 10 { 11 delegate string GetString(float num, int num2); 12 13 static void Main(string[] args) 14 { 15 //使用表达式进行简写 16 GetString m = (num, num2) => num.ToString() + "," + num2.ToString(); 17 Func(m, 10.5f, 100); 18 19 //直接传递表达式 20 Func((num, num2) => num.ToString() + "," + num2.ToString(), 20.5f, 200); 21 22 Console.ReadKey(); 23 } 24 25 static void Func(GetString method, float num, int num2) 26 { 27 Console.WriteLine("Func: " + method(num, num2)); 28 } 29 } 30 }
下面是运行的结果:
1 Func: 10.5,100 2 Func: 20.5,200
这里引入了一个新的知识点协变和抗变,大家可以自行搜索,或者查看协变和抗变的文章点击这里。
事件:
C#里的事件使用event关键字定义,无需实例化就可以使用,可以将其看做一个特殊的委托对象,下面我们看看一个例子。
EventDispatcher.cs(用来定义和发送特定事件的类):
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Test 8 { 9 /// <summary> 10 /// 事件发送类. 11 /// </summary> 12 class EventDispatcher 13 { 14 /// <summary> 15 /// 定义特定的事件委托, 注意不要有返回值. 16 /// </summary> 17 /// <param name="sender">发送者.</param> 18 /// <param name="args">附带的参数.</param> 19 public delegate void MyEventHandler(object sender, MyEventArgs args); 20 21 /// <summary> 22 /// 事件对象, 所有的回调都可以添加到该对象上, 不需要实例化就能使用. 23 /// </summary> 24 public event MyEventHandler onCustom; 25 26 /// <summary> 27 /// 构造函数. 28 /// </summary> 29 public EventDispatcher() 30 { 31 } 32 33 /// <summary> 34 /// 发送一个自定义事件. 35 /// </summary> 36 /// <param name="data">数据.</param> 37 public void dispatchCustom(String data) 38 { 39 onCustom(this, new MyEventArgs(data)); 40 } 41 } 42 43 /// <summary> 44 /// 自定义事件参数类. 45 /// </summary> 46 class MyEventArgs : EventArgs 47 { 48 public String data; 49 50 public MyEventArgs(String data) 51 { 52 this.data = data; 53 } 54 } 55 }
Program.cs(主程序,测试类):
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Test 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 new Program(); 14 15 Console.ReadKey(); 16 } 17 18 public Program() 19 { 20 EventDispatcher ed = new EventDispatcher(); 21 22 //第一种方式 23 EventDispatcher.MyEventHandler meh = new EventDispatcher.MyEventHandler(CustomHandler); 24 ed.onCustom += meh; 25 ed.dispatchCustom("Hello One!"); 26 ed.onCustom -= meh; 27 28 //第二种方式 29 ed.onCustom += new EventDispatcher.MyEventHandler(CustomHandler); 30 ed.dispatchCustom("Hello Two!"); 31 //下面两种方式都可以删除注册的事件处理函数 32 ed.onCustom -= new EventDispatcher.MyEventHandler(CustomHandler); 33 //ed.onCustom -= CustomHandler; 34 35 //简写方式 36 ed.onCustom += CustomHandler; 37 ed.dispatchCustom("Hello Three!"); 38 ed.onCustom -= CustomHandler; 39 40 //匿名函数写法 41 ed.onCustom += delegate(object sender, MyEventArgs args) 42 { 43 Console.WriteLine("Event Handler (delegate) : " + args.data); 44 }; 45 ed.dispatchCustom("Hello Four!"); 46 47 //Lambda 写法 48 ed.onCustom += (sender, args) => 49 { 50 Console.WriteLine("Event Handler (lambda) : " + args.data); 51 }; 52 ed.dispatchCustom("Hello Five!"); 53 54 //简写 Lambda 55 ed.onCustom += (sender, args) => Console.WriteLine("Event Handler (lambda) : " + args.data); 56 ed.dispatchCustom("Hello six!"); 57 } 58 59 private void CustomHandler(object sender, MyEventArgs args) 60 { 61 Console.WriteLine("Event Handler : " + args.data); 62 } 63 } 64 }
下面是程序运行的结果:
1 Event Handler : Hello One! 2 Event Handler : Hello Two! 3 Event Handler : Hello Three! 4 Event Handler (delegate) : Hello Four! 5 Event Handler (delegate) : Hello Five! 6 Event Handler (lambda) : Hello Five! 7 Event Handler (delegate) : Hello six! 8 Event Handler (lambda) : Hello six! 9 Event Handler (lambda) : Hello six!