(一)引用方法
委托是寻址方法的.NET版本。委托是类型安全的类,它定义了返回类型和参数的类型。委托不仅包含对方法的引用,也可以包含对多个方法的引用。
Lambda表达式与委托直接相关。当参数是委托类型时,就可以使用lambda表达式实现委托引用的方法。
(二)委托
当要把方法传递给其它方法时,需要使用委托。委托是一种特殊类型的对象,其特殊之处在于,我们以前定义的所有对象都包含数据,而委托包含的只是一个或多个方法的地址。
1、声明委托
委托使用关键字 delegate 进行定义。
例子:
定义一个返回类型为void参数为一个int的名为IntMethodInvoker的委托
delegate void IntMethodInvoker(int x);
因为定义委托基本上就是定义一个新类,所以可以在定义类的任何相同地方定义委托。可以在委托定义上应用常见的访问修饰符:public、private、protected等。
2、使用委托
1 delegate int CalculateMethodInvoker(int x, int y); 2 class Program 3 { 4 static void Main(string[] args) 5 { 6 CalculateMethodInvoker calculateMethodInvoker = CalculateMethodHelper.Sum; 7 int x = 100, y = 200; 8 Console.WriteLine("x,y相加:{0}", Calculate(calculateMethodInvoker, x, y)); 9 calculateMethodInvoker = CalculateMethodHelper.Multiply; 10 Console.WriteLine("x,y相乘:{0}", Calculate(calculateMethodInvoker, x, y)); 11 Console.ReadKey(); 12 } 13 public static int Calculate(CalculateMethodInvoker calculateMethodInvoker, int x, int y) 14 { 15 return calculateMethodInvoker(x, y); 16 } 17 } 18 public class CalculateMethodHelper 19 { 20 public static int Sum(int x, int y) 21 { 22 return x + y; 23 } 24 public static int Multiply(int x, int y) 25 { 26 return x * y; 27 } 28 }
运行以上代码,结果如下:
为了减少输入量,只需要委托实例,就可以只传递地址的名称。这称为委托推断。
3、Action<T>和Func<T>委托
除了为每个参数和返回类型定义一个新的委托类型外,还可以使用Action<T>和Func<T>委托。
泛型Action<T>委托表示引用一个void返回类型的方法,没有泛型参数的Action类可调用没有参数的方法。
泛型Func<T>委托表示引用一个有返回值的方法。
4、多播委托
委托也可以包含多个方法。这种委托成为多播委托。如果调用多播委托,就可以按顺序连续调用多个方法。为此,委托的签名就必须返回void;否则,就只能得到委托调用的最后一个方法的结果。多播委托识别运算符“-”、“+”、“-=”、“+=”以从委托中增加或删除方法调用。
例子:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Action<int, int> calFunc = CalculateMethodHelper.Sum; 6 calFunc += CalculateMethodHelper.Multiply; 7 int x = 100, y = 200; 8 Calculate(calFunc, x, y); 9 Console.ReadKey(); 10 } 11 public static void Calculate(Action<int, int> calculateMethodInvoker, int x, int y) 12 { 13 Console.WriteLine("运行结果:"); 14 calculateMethodInvoker(x, y); 15 } 16 } 17 public class CalculateMethodHelper 18 { 19 public static void Sum(int x, int y) 20 { 21 Console.WriteLine("x,y相加:{0}", x + y); 22 } 23 public static void Multiply(int x, int y) 24 { 25 Console.WriteLine("x,y相乘:{0}", x * y); 26 } 27 }
如果通过委托调用的其中一个方法抛出异常,整个迭代就会停止。解决的方法是,使用Delegate类中定义的GetInvocationList()方法获取Delegate对象数组,再使用循环遍历执行,在过程中捕获异常,来继续下一次迭代。
5、匿名方法
匿名方法是用作委托的参数的一段代码。
例子:
Action<int, int> calFunc = delegate (int i, int j) { Console.WriteLine("x,y相加:{0}", i + j); };
在匿名方法中不可使用跳转语句(break、goto或continue),在匿名方法内部不能访问不安全代码,不能访问在匿名方法外部使用的ref和out参数。
(三)lambda表达式
自C#3.0开始,可以使用新的语法把实现代码赋予委托,只要有委托参数类型的地方,就可以使用lambda表达式。
例子:
Action<int, int> calFunc = (i, j) => { Console.WriteLine("x,y相加:{0}", i + j); };
1、参数
lambda表达式有几种定义参数的方式。如果只有一个参数,只写出参数名就足够了。如果除一个参数以外,需要圆括号把参数名括起来。
例子:
Action<int> one = i => { //方法内容 }; Action<int, int> two = (i, j) => { //方法内容 };
2、多行代码
如果lambda表示只有一条语句,在方法块内就不需要花括号和return语句,因为编译器会隐式添加return。
例子:
Func<int> lambdaOne = () => 0;
如果实现代码超过一行,就需要使用return语句显式返回。
例子:
Func<int> lambdaOne = () => { int i = 0; i++; ++i; return i; };
3、闭包
通过lambda表达式可以访问lambda表达式块外部的变量。这称为闭包。
例子:
int param = 10; Action<int> lambdaSecond = (i) => { Console.WriteLine(i + param); }; lambdaSecond(3); Console.ReadKey();
运行以上代码,结果如下:
(四)事件
事件基于委托,为委托提供了一种发布/订阅机制。
例子:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 AlarmClock alarmClock = new AlarmClock(); 6 Student zsStudent = new Student("张三"); 7 alarmClock.ItsGetUpClockEvent += zsStudent.ItsGetUpClock; 8 alarmClock.ItsGetUpClock(); 9 Student lsStudent = new Student("李四"); 10 WeakEventManager<AlarmClock, EventArgs>.AddHandler(alarmClock, "ItsGetUpClockEvent", lsStudent.ItsGetUpClock);//弱事件 11 alarmClock.ItsGetUpClock(); 12 Console.ReadKey(); 13 } 14 15 } 16 //事件发布类 17 public class AlarmClock 18 { 19 public event EventHandler<EventArgs> ItsGetUpClockEvent; 20 public void ItsGetUpClock() 21 { 22 Console.WriteLine("时间到,起床了!"); 23 ItsGetUpClockEvent?.Invoke(this, new EventArgs()); 24 } 25 } 26 //事件侦听类 27 public class Student 28 { 29 public string Name { get; set; } 30 public Student(string name) 31 { 32 this.Name = name; 33 } 34 public void ItsGetUpClock(object sender, EventArgs e) 35 { 36 Console.WriteLine("{0}关掉闹钟,起床了。",Name); 37 } 38 }