04.C#委托、Lambda表达式

1.委托

(1)委托的本质

委托实际上就是指向函数的指针。在C#中委托是一种类型,定义一个委托就是定义一个新的类,它与类的地位是一样的,所以可以定义类的地方都可以定义委托!

实际上,使用delegate关键字定义的委托继承了System.MulticastDelegate类,而System.MulticastDelegate类又继承了System.Delegate。

Delegate:表示委托,委托是一种数据结构,它引用静态方法或引用类实例及该类的实例方法。

MulticastDelegate:表示多路广播委托;即,其调用列表中可以拥有多个元素的委托。

(2)使用委托的示例

  1. delegate string DelegateMethod(int a, int b);//定义委托
  2. class Program
  3. {
  4. static void Main(string[] args)
  5. {
  6. DelegateMethod dm = new DelegateMethod(Method);//或:DelegateMethod dm = Method
  7. Console.WriteLine(dm(10, 20));//执行委托
  8. Console.Read();
  9. }
  10. private static string Method(int a, int b)
  11. {
  12. return "相加结果:" + (a + b);
  13. }
  14. }

(3)委托的Invoke与BeginInvoke方法

1、Invoke方法

实际上,给委托实例提供圆括号与调用委托的Invoke方法效果完全相同:

  1. delegate string DelegateMethod(int a, int b);//定义委托
  2. class Program
  3. {
  4. static void Main(string[] args)
  5. {
  6. DelegateMethod dm = Method;
  7. Console.WriteLine(dm.Invoke(10, 20));//执行委托
  8. Console.Read();
  9. }
  10. private static string Method(int a, int b)
  11. {
  12. return "相加结果:" + (a + b);
  13. }
  14. }

2、BeginInvoke方法

调用委托实例的BeginInvoke方法就是开启一个新的线程执行委托实例指向的方法。

  1. delegate void DelegateMethod(int a, int b);//定义委托
  2. class Program
  3. {
  4. static void Main(string[] args)
  5. {
  6. DelegateMethod dm = Method;
  7. for (int i = 0; i < 10; i++)
  8. {
  9. dm.BeginInvoke(i, 20, null, null);//异步调用委托
  10. }
  11. Console.Read();
  12. }
  13. private static void Method(int a, int b)
  14. {
  15. Console.WriteLine("相加结果:" + (a + b));//执行委托
  16. }
  17. }

BeginInvoke方法有三种参数,第一种参数是委托实例指向的方法的参数(可能有多个);第二种参数的类型是AsyncCallback,AsyncCallback是一个委托类型,其定义是delegate void AsyncCallback(IAsyncResult ar),当调用BeginInvoke方法的委托实例异步执行完成时,就会执行该参数指向的方法;第三种参数是object类型,主要是向第二种参数传递一些值,一般可以传递被调用方法的委托,这个值可以使用IAsyncResult.AsyncState属性获得。(具体请参考“C#多线程”)

  1. delegate void DelegateMethod(int a, int b);//定义委托
  2. class Program
  3. {
  4. static void Main(string[] args)
  5. {
  6. DelegateMethod dm = Method;
  7. dm.BeginInvoke(10, 20, MethodCompleted, null);//异步调用委托
  8. Console.Read();
  9. }
  10. //异步委托
  11. private static void Method(int a, int b)
  12. {
  13. Console.WriteLine("相加结果:" + (a + b));
  14. Thread.Sleep(3000);
  15. }
  16. //回调函数
  17. private static void MethodCompleted(IAsyncResult ar)
  18. {
  19. Console.WriteLine("休眠结束!");
  20. }
  21. }

3、委托的EndInvoke方法

如果调用了委托实例的BeginInvoke方法,就可以通过EndInvoke方法获得委托实例指向的方法的返回值,或是确定指向的方法已经被成功调用。(具体请参考“C#多线程”)

  1. delegate string DelegateMethod(int a, int b);//定义委托
  2. class Program
  3. {
  4. static void Main(string[] args)
  5. {
  6. DelegateMethod dm = Method;
  7. IAsyncResult ar = dm.BeginInvoke(10, 20, null, null);//异步调用委托
  8. string result = dm.EndInvoke(ar);//等待委托异步调用结束
  9. Console.WriteLine(result);//输出返回值
  10. Console.Read();
  11. }
  12. //异步委托
  13. private static string Method(int a, int b)
  14. {
  15. Thread.Sleep(3000);
  16. return "相加结果:" + (a + b);
  17. }
  18. }

(4)Action<T>、Func<T>与Predicate<T>(泛型委托)

除了为每个参数和返回类型定义一个新委托之外,还可以使用Action<T>与Func<T>泛型委托。使用泛型委托主要的优势是可以省略委托的定义。

1、Action<T>无返回值的泛型委托

泛型Action<T>委托表示引用一个void返回类型的方法,这个委托类可以有多个(≥0)参数且参数的类型可以不同(最多可以有16种不同类型的参数)。

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. Action<int,int> dm = Method;//委托实例
  6. dm(10,20);//调用委托
  7. Console.Read();
  8. }
  9. private static void Method(int a, int b)
  10. {
  11. Console.WriteLine("相加结果:" + (a + b));
  12. }
  13. }

2、Func<T>带有返回值的泛型委托

泛型Func<T>委托与Action<T>委托类似,不同点是Func<T>允许调用带返回值类型的方法,而且其返回值类型的指定是Func<T1,T2,T3...Tn>中的最后一个类型(Tn),即Tn就是其返回值,其它类型都表示参数的类型。(最多可以有16种参数类型和一个返回值类型)

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. Func<int, int, string> dm = Method;//委托实例
  6. string result = dm(10, 20);//调用委托
  7. Console.WriteLine(result);//输出返回值
  8. Console.Read();
  9. }
  10. private static string Method(int a, int b)
  11. {
  12. return "相加结果:" + (a + b);
  13. }
  14. }

3、Predicate<T>委托(常用于集合参数)

Predicate<T>泛型委托,只能接受一个传入参数,返回值为bool类型。

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. Predicate<int> dm = Method;//委托实例
  6. if (dm(12))//调用委托
  7. {
  8. Console.WriteLine("偶数");
  9. }
  10. else
  11. {
  12. Console.WriteLine("奇数");
  13. }
  14. Console.Read();
  15. }
  16. private static bool Method(int a)
  17. {
  18. if(a%2==0)
  19. {
  20. return true;
  21. }
  22. return false;
  23. }
  24. }

(5)多播委托

前面的委托只包含一个方法,但是委托也可以包含多个方法,这种委托称为多播委托。如果调用多播委托就可以按顺序连续调用多个方法,为此委托的定义就必须返回void,否则就只能得到委托调用的最后一个方法的结果。多播委托可以识别“+、-、+=、-=”运算符。

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. Action<int> dm = null; //委托实例
  6. for (int i = 0; i < 20; i++)
  7. {
  8. if (i % 2 == 0)
  9. {
  10. dm += Method1;
  11. }
  12. else
  13. {
  14. dm += Method2;
  15. }
  16. }
  17. dm(123456);//调用多播委托
  18. Console.Read();
  19. }
  20. private static void Method1(int a)
  21. {
  22. Console.WriteLine("委托1:" + a);
  23. }
  24. private static void Method2(int a)
  25. {
  26. Console.WriteLine("委托2:" + a);
  27. }
  28. }

(6)使用委托定义匿名方法

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. Action<int> dm = delegate(int a)
  6. {
  7. Console.WriteLine(a.ToString());
  8. };//匿名方法
  9. dm(123456);//调用委托指向的匿名方法
  10. Console.Read();
  11. }
  12. }

2.Lambda表达式

(1)Lambda表达式本质

Lambda表达式的本质是一个匿名函数,Lambda表达式只能与委托配合使用,其优势在可以很方便的定义匿名方法。所有Lambda表达式都使用Lambda运算符=>,该运算符读作"goes to"。Lambda运算符的左边是输入参数(如果有),右边是表达式或语句块。

(2)Lambda表达式使用示例

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. //标准的Lambda格式
  6. Func<int, int, string> sum0 = (int a, int b) => { return "和是:" + (a + b); };
  7. Console.WriteLine(sum0(10, 20));
  8. //简写格式
  9. Func<int, int, string> sum1 = (a, b) => "和是:" + (a + b);
  10. Console.WriteLine(sum1(30, 40));
  11. Action<int, int> sum2 = (a, b) => Console.WriteLine("和是:" + (a + b));
  12. sum2(50, 60);
  13. Console.Read();
  14. }
  15. }

(3)Lambda表达式语法

1、表达式Lambda语法(参数语法)

如果只有一个输入参数时,括号可以省略,如:(x) => x * x 等于 x => x * x
    如果具有一个以上的输入参数,必需加上括号,如:(x, y) => x
== y
    可以显式指定输入参数的类型,如:(int x, string
s) => s.Length > x
    也可以没有任何输入参数,如:() => Console.WriteLine("无参数的Lambda表达式")

2、语句Lambda语法(函数体语法)

语句写在大括号中,如:

  1. Action<int, int> sum = (a, b) => { int i = a = +b; Console.Write(i); };

只有一条语句时,可以省略大括号“{}”,如:

  1. Action<int, int> sum = (a, b) => Console.Write(a + b);

当匿名方法有返回值时,可以使用return,如:

  1. Func<int, int, string> sum0 = (a, b) => { return "和是:" + (a + b); };

当匿名方法有返回值,且只有一条语句时,可以省略大括号“{}”和return,如:

  1. Func<int, int, string> sum1 = (a, b) => "和是:" + (a + b);

注意:当有返回值时,语句Lambda中使用了大括号就一定要使用return关键字返回;反之使用了return关键字返回结果就一定要使用大括号。即:return关键字与大括号必须同时省略!

3、类型猜测

当编写一个Lambda式的时候,我们通常不需要明确指定输入参数的类型。因为编译器会根据Lambda体的实现,以及委托的定义来猜测类型。如:如果要从一个List<int>中删除小于100的元素

  1. List<int> list = new List<int>();
  2. list.RemoveAll(i => i < 100);//i会被猜测为int

通常的猜测规则如下:

  1. Lambda式必须包含与委托定义中相等数量的输入参数;
  2. 每个Lambda式的输入参数必须能够隐式转换成委托定义中所要求的输入参数;
  3. Lambda式的返回值必须能够隐式转换成委托定义中的返回值。

4、Lambda式中的变量作用域

在Lambda式定义中可以引用外部变量。只要是在定义处能够访问到的变量,都可以在Lambda式中引用。所有会被引用的外部变量必须在Lambda式定义之前被显式赋值。

变量作用域的规则:

  1. 被“捕获”的变量在委托的生命周期结束前都不会被垃圾回收;
  2. 在Lambda式内部定义的变量对外不可见;
  3. Lambda式无法直接捕获一个具有ref或out描述的参数变量;
  4. Lambda式中的return语句不会导致当前所在的方法返回;
  5. Lambda式中不允许包含会导致跳当前执行范围的goto,break 或 continue语句。

-------------------------------------------------------------------------------------------------------------------------------

来自为知笔记(Wiz)

时间: 2024-11-01 20:37:57

04.C#委托、Lambda表达式的相关文章

个人总结:匿名委托+内置委托+Lambda表达式

匿名委托+内置委托+Lambda表达式 概要 在实际开发中,项目开发框架不同,用到的技术是不一样的,这里总结一下用Linq开发,ORM之EF开发会大量使用的Lambda表达式; 正文 之前提过.Net有一套扩展在IEumberable<T>泛型接口的扩展方法,本文是从这个基础继续总结; 1.void Action<in T>: 先拿一个ForEach()来讲: public void ForEach(Action<T> action); 这个Action<T>

委托 lambda表达式浅显理解

方法不能跟变量一样当参数传递,怎么办,C#定义了委托,就可以把方法当变量一样传递了,为了简单,匿名方法传递,省得再声明方法了:再简单,lambda表达式传递,比匿名方法更直观. public delegate int delegateArithmetic(int a, int b); //委托作为参数,方法传递给委托 public int result(int x,int y,delegateArithmetic delAri) { return delAri(x, y); } public i

C# 委托,Lambda表达式,事件

本文章参考sikiC#高级篇,转载请注明出处. 什么是委托 如果我们要把方法当做参数来传递的话,就要用到委托.简单来说委托是一个类型,这个类型可以赋值一个方法的引用. 声明委托与使用 声明委托有四种方式.一种是原生的,另外三种是C#为了方便给我们封装好的. 四种声明方式分别为delegate, Action, Func, 在C#中使用一个类分为两个阶段,首先定义这个类,告诉编译器这个类由什么字段和方法组成,然后使用这个类实例化对象.在我们使用委托的时候,也需要经过这两个阶段,首先定义委托,告诉编

Lambda表达式演变

Lambda表达式是一种匿名函数. 演变步骤: 一般的方法委托 => 匿名函数委托 => Lambda表达式 Lambda表达式其实并不陌生,他的前生就是匿名函数,所以要谈Lambda表达式,就不得不谈匿名函数,要谈匿名函数,那又要不得不谈委托. 委托非常好理解,类似于C++里面的函数指针(指向了一个方法),并且委托约束了待指向方法的签名(由返回类型和参数组成). 首先,定义一个委托如 : delegate int ProcessInt(int i); 定义了委托可以像变量一样传递方法.如:

委托、Lambda表达式、事件系列04,委托链是怎样形成的, 多播委托

在"委托.Lambda表达式.事件系列01,委托是什么,委托的基本用法,委托的Method和Target属性"中,反编译委托,发现委托都是多播委托. 既然委托是多播委托,我们可以通过"+="把多个方法赋给委托变量,这样就形成了一个委托链, 它是怎样形成的?来看下面的例子: namespace ConsoleApplication3 { internal delegate void MySayDel(string msg); class Program { stati

.NET中那些所谓的新语法之三:系统预定义委托与Lambda表达式

开篇:在上一篇中,我们了解了匿名类.匿名方法与扩展方法等所谓的新语法,这一篇我们继续征程,看看系统预定义委托(Action/Func/Predicate)和超爱的Lambda表达式.为了方便码农们,.Net基类库针对实际开发中最常用的情形提供了几个预定义好的委托,这些委托可以直接使用,无需再重头定义一个自己的委托类型.预定义委托在.Net基类库中使用的比较广泛,比如在Lambda表达式和并行计算中都大量地使用,需要我们予以关注起来! /* 新语法索引 */ 1.自动属性 Auto-Impleme

委托、Lambda表达式、事件系列07,使用EventHandler委托

谈到事件注册,EventHandler是最常用的. EventHandler是一个委托,接收2个形参.sender是指事件的发起者,e代表事件参数. □ 使用EventHandler实现猜拳游戏 使用EventHandler实现一个猜拳游戏,每次出拳,出剪刀.石头.布这三者的其中一种. 首先抽象出一个被观察者,其中提供了事件,提供了执行事件的方法. public class FistGame { public string FistName { get; set; } public event

委托、Lambda表达式、事件系列05,Action委托与闭包

来看使用Action委托的一个实例: static void Main(string[] args) { int i = 0; Action a = () => i++; a(); a(); Console.WriteLine(i); } 结果是期望能的2.但令人好奇的是:栈上的变量i是如何传递给Action委托的? 反编译进行查看,首先看Main方法对应的IL代码: 再看c_DisplayClass1的IL代码: 从中可以看出:→在托管堆上创建了一个名为c_DisplayClass1的实例→把

委托、Lambda表达式、事件系列06,使用Action实现观察者模式

在"实现观察者模式(Observer Pattern)的2种方式"中,曾经通过接口的方式.委托与事件的方式实现过观察者模式.本篇体验使用Action实现此模式. 就举一个足球场上的例子,当裁判吹响终场哨,胜队庆祝,失败队落寞.把裁判看作是被观察者,比赛中的两队看作是观察者. 裁判作为被观察者需要提供一个Action委托供观察者方法注册. public class Referee { public Action DoSth; public void ISayGameOver() { Co