C# 委托、事件,lamda表达式

1. 委托Delegate

C#中的Delegate对应于C中的指针,但是又有所不同C中的指针既可以指向方法,又可以指向变量,并且可以进行类型转换,

C中的指针实际上就是内存地址变量,他是可以直接操作内存的,通过内存地址直接访问变量,直接调用方法。

而C#中的Delegate是强类型的,也就是说在声明委托时就已经指定了该变量只能指向具有特定参数,以及返回值的方法。

使用delegate就可以直接建立任何名称的委托类型,当进行系统编译时,系统就会自动生成此类型。您可以使用delegate void MyDelegate() 方式建立一个委托类,并使用ILDASM.exe观察其成员。由ILDASM.exe 中可以看到,它继承了System.MulticastDelegate类,

并自动生成BeginInvoke、EndInvoke、Invoke 等三个常用方法。

Invoke 方法是用于同步调用委托对象的对应方法,而BeginInvoke、EndInvoke是用于以异步方式调用对应方法的。


1

2

3

4

5

6

7

8

public class MyDelegate:MulticastDelegate

      {

          //同步调用委托方法

          public virtual void Invoke();

          //异步调用委托方法

          public virtual IAsyncResult BeginInvoke(AsyncCallback callback,object state);

          public virtual void EndInvoke(IAsyncResult result);

      }

MulticastDelegate是System.Delegate的子类,它是一个特殊类,编译器和其他工具可以从此类派生,但是自定义类不能显式地从此类进行派生。它支持多路广播委托,并拥有一个带有链接的委托列表,在调用多路广播委托时,系统将按照调用列表中的委托出现顺序来同步调用这些委托。

MulticastDelegate具有两个常用属性:Method、Target。其中Method 用于获取委托所表示的方法Target 用于获取当前调用的类实例。

1.1 委托的使用

当建立委托对象时,委托的参数类型必须与委托方法相对应。只要向建立委托对象的构造函数中输入方法名称example.Method,委托就会直接绑定此方法。使用myDelegate.Invoke(string message),就能显式调用委托方法。但在实际的操作中,我们无须用到 Invoke 方法,而只要直接使用myDelegate(string message),就能调用委托方法。

无返回值的委托


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

class Program

     {

         delegate void MyDelegate(string message);

         public class Example

         {

             public void Method(string message)

             {

                 MessageBox.Show(message);

             }

         }

         static void Main(string[] args)

         {

             Example example=new Example();

             MyDelegate myDelegate=new MyDelegate(example.Method);

             myDelegate("Hello World");

             Console.ReadKey();

         }

     }

有返回值的委托


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

class Program

     {

         delegate string MyDelegate(string message);

         public class Example

         {

             public string Method(string name)

             {

                 return "Hello " + name;

             }

         }

         static void Main(string[] args)

         {

             Example example=new Example();

             //绑定委托方法

             MyDelegate myDelegate=new MyDelegate(example.Method);

             //调用委托,获取返回值

             string message = myDelegate("Leslie");

             Console.WriteLine(message);

             Console.ReadKey();

         }

     }

多路广播委托


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

delegate double MyDelegate(double message);

         public class Price

         {

             public double Ordinary(double price)

             {

                 double price1 = 0.95 * price;

                 Console.WriteLine("Ordinary Price : "+price1);

                 return price1;

             }

             public double Favourable(double price)

             {

                 double price1 = 0.85 * price;

                 Console.WriteLine("Favourable Price : " + price1);

                 return price1;

             }

             static void Main(string[] args)

             {

                 Price price = new Price();

                 //绑定Ordinary方法

                 MyDelegate myDelegate = new MyDelegate(price.Ordinary);

                 //绑定Favourable方法

                 myDelegate += new MyDelegate(price.Favourable);

                 //调用委托

                 Console.WriteLine("Current Price : " + myDelegate(100));

                 Console.ReadKey();

             }

         }

输出

1.2 委托的协变与逆变

前面已经说过,委托是强类型的方法指针,但是在面对具有继承关系类型的参数、或者返回值时,委托是如何处理的呢。

协变(返回值类型具有继承关系的方法)


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

public class Worker

      {.......}

      public class Manager:Worker

      {.......}

 

       class Program

      {

          public delegate Worker GetWorkerHandler(int id);

          //在 Framework2.0 以上,委托 GetWorkerHandler 可绑定 GetWorker 与 GetManager 两个方法 

          public static Worker GetWorker(int id)

          {

              Worker worker = new Worker();

              return worker;

          }

 

          public static Manager GetManager(int id)

          {

              Manager manager = new Manager();

              return manager;

          }

 

         static void Main(string[] args)

         {

             GetWorkerHandler workerHandler = new GetWorkerHandler(GetWorker);

             Worker worker=workerHandler(1);

             GetWorkerHandler managerHandler = new GetWorkerHandler(GetManager);

             Manager manager = managerHandler(2) as Manager;

             Console.ReadKey();

         }

      }

委托 GetWorkerHandler 可以绑定 GetWorker 与 GetManager 两个方法

逆变

委托逆变,是指委托方法的参数同样可以接收 “继承” 这个传统规则。像下面的例子,以 object 为参数的委托,可以接受任何 object 子类的对象作为参数。最后可以在处理方法中使用 is 对输入数据的类型进行判断,分别处理对不同的类型的对象。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

class Program

     {

         public delegate void Handler(object obj);

         public static void GetMessage(object message)

         {

             if (message is string)

                 Console.WriteLine("His name is : " + message.ToString());

             if (message is int)

                 Console.WriteLine("His age is : " + message.ToString());

         }

         static void Main(string[] args)

         {

             Handler handler = new Handler(GetMessage);

             handler(29);

             Console.ReadKey();

         }

    }

注意委托与其绑定方法的参数必须一至,即当 Handler 所输入的参数为 object 类型,其绑定方法 GetMessage 的参数也必须为 object 。否则,即使绑定方法的参数为 object 的子类,系统也无法辨认。

大家可能注意到了,这个委托方法GetMessage的实现不是那么优雅,于是泛型委托应运而生。

class Program { public delegate void Handler<T>(T obj); public static void GetWorkerWages(Worker worker) { Console.WriteLine("Worker‘s total wages is " + worker.Wages); } public static void GetManagerWages(Manager manager) { Console.WriteLine("Manager‘s total wages is "+manager.Wages); } static void Main(string[] args) { Handler<Worker> workerHander = new Handler<Worker>(GetWorkerWages); Worker worker = new Worker(); worker.Wages = 3000; workerHander(worker); Handler<Manager> managerHandler = new Handler<Manager>(GetManagerWages); Manager manager = new Manager(); manager.Wages = 4500; managerHandler(manager); Console.ReadKey(); } }

2. event事件的由来

事件是特殊的委托,他为委托提供了封装性,一方面允许从类的外部增加,删除绑定方法,另一方面又不允许从类的外部来触发委托所绑定了方法。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

public delegate double PriceHandler();

     public class PriceManager

     {

         public PriceHandler GetPriceHandler;

         //委托处理,当价格高于100元按8.8折计算,其他按原价计算

         public double GetPrice()

         {

             if (GetPriceHandler.GetInvocationList().Count() > 0)

             {

                 if (GetPriceHandler() > 100)

                     return GetPriceHandler()*0.88;

                 else

                     return GetPriceHandler();

             }

             return -1;

         }

     }

     class Program

     {

         static void Main(string[] args)

         {

             PriceManager priceManager = new PriceManager();

            

             //调用priceManager的GetPrice方法获取价格

             //直接调用委托的Invoke获取价格,两者进行比较

             priceManager.GetPriceHandler = new PriceHandler(ComputerPrice);

             Console.WriteLine(string.Format("GetPrice\n  Computer‘s price is {0}!",

                 priceManager.GetPrice()));

             Console.WriteLine(string.Format("Invoke\n  Computer‘s price is {0}!",

                 priceManager.GetPriceHandler.Invoke()));

            

             Console.WriteLine();

            

             priceManager.GetPriceHandler = new PriceHandler(BookPrice);

             Console.WriteLine(string.Format("GetPrice\n  Book‘s price is {0}!",

                 priceManager.GetPrice()));

             Console.WriteLine(string.Format("Invoke\n  Book‘s price is {0}!" ,

                 priceManager.GetPriceHandler.Invoke()));

            

             Console.ReadKey();

         }

         //书本价格为98元

         public static double BookPrice()

         {

             return 98.0;

         }

         //计算机价格为8800元

         public static double ComputerPrice()

         {

             return 8800.0;

         }

     }

以上代码实现了对于100元以上商品的的88折处理。一方面为了给GetPriceHandler绑定方法就必须将委托声明为public,但是一旦声明为public

就可以在类外部直接通过Invoke来调用该委托所绑定的方法,而产生我们不需要的结果。

当然我们可以将GetPriceHandler声明为private并且通过public 的addHandler,removeHandler来消除委托public的副作用,但是C#提供了更加优雅的方法:

那就是event关键字。

事件(event)可被视作为一种特别的委托,它为委托对象隐式地建立起add_XXX、remove_XXX 两个方法,用作注册与注销事件的处理方法。而且事件对应的变量成员将会被视为 private 变量,外界无法超越事件所在对象直接访问它们,这使事件具备良好的封装性,而且免除了add_XXX、remove_XXX等繁琐的代码。


1

2

3

4

5

public class EventTest

     {

         public delegate void MyDelegate();

         public event MyDelegate MyEvent;

     }

观察事件的编译过程可知,在编译的时候,系统为 MyEvent 事件自动建立add_MyEvent、remove_MyEvent 方法。

事件能通过+=和-=两个方式注册或者注销对其处理的方法,使用+=与-=操作符的时候,系统会自动调用对应的 add_XXX、remove_XXX 进行处理。 值得留意,在PersonManager类的Execute方法中,如果 MyEvent 绑定的处理方法不为空,即可使用MyEvent(string)引发事件。但如果在外界的 main 方法中直接使用 personManager.MyEvent (string) 来引发事件,系统将引发错误报告。这正是因为事件具备了良好的封装性,使外界不能超越事件所在的对象访问其变量成员。

注意在事件所处的对象之外,事件只能出现在+=,-=的左方。

public delegate void MyDelegate(string name);      public class PersonManager      {          public event MyDelegate MyEvent;          //执行事件          public void Execute(string name)          {              if (MyEvent != null)                  MyEvent(name);          }      }      class Program      {          static void Main(string[] args)          {              PersonManager personManager = new PersonManager();              //绑定事件处理方法              personManager.MyEvent += new MyDelegate(GetName);              personManager.Execute("Leslie");              Console.ReadKey();          }          public static void GetName(string name)          {              Console.WriteLine("My name is " + name);          }      }

在绑定事件处理方法的时候,事件出现在+=、-= 操作符的左边,对应的委托对象出现在+=、-= 操作符的右边。对应以上例子,事件提供了更简单的绑定方式,只需要在+=、-= 操作符的右方写上方法名称,系统就能自动辩认。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

public delegate void MyDelegate(string name);

     public class PersonManager

     {

         public event MyDelegate MyEvent;

         .........

     }

     class Program

     {

         static void Main(string[] args)

         {

             PersonManager personManager = new PersonManager();

             //绑定事件处理方法

             personManager.MyEvent += GetName;

             .............

         }

         public static void GetName(string name)

         {.........}

    }

如果觉得编写 GetName 方法过于麻烦,你还可以使用匿名方法绑定事件的处理。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

public delegate void MyDelegate(string name);

     public class PersonManager

     {

         public event MyDelegate MyEvent;

         //执行事件

         public void Execute(string name)

         {

             if (MyEvent != null)

                 MyEvent(name);

         }

         static void Main(string[] args)

         {

             PersonManager personManager = new PersonManager();

             //使用匿名方法绑定事件的处理

             personManager.MyEvent += delegate(string name){

                 Console.WriteLine("My name is "+name);

             };

             personManager.Execute("Leslie");

             Console.ReadKey();

         }

     }


1
 

3. lambda表达式

在Framework 2.0 以前,声明委托的唯一方法是通过方法命名,从Framework 2.0 起,系统开始支持匿名方法。 通过匿名方法,可以直接把一段代码绑定给事件,因此减少了实例化委托所需的编码系统开销。 而在 Framework 3.0 开始,Lambda 表达式开始逐渐取代了匿名方法,作为编写内联代码的首选方式。总体来说,Lambda 表达式的作用是为了使用更简单的方式来编写匿名方法,彻底简化委托的使用方式。

使用匿名方法


1

2

3

4

5

6

7

static void Main(string[] args)

         {

             Button btn = new Button();

             btn.Click+=delegate(object obj,EventArgs e){

                 MessageBox.Show("Hello World !");

             };

         }

使用lambda表达式


1

2

3

4

5

6

7

static void Main(string[] args)

         {

             Button btn = new Button();

             btn.Click+=(object obj,EventArgs e)=>{

                 MessageBox.Show("Hello World !");

             };

         }

3.1常用泛型委托

public delegate bool Predicate<T>(T obj)

它是一个返回bool的泛型委托,能接受一个任意类型的对象作为参数。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

class Program

     {

         static void Main(string[] args)

         {

             List<Person> list = GetList();

             //绑定查询条件

             Predicate<Person> predicate = new Predicate<Person>(Match);

             List<Person> result = list.FindAll(predicate);

             Console.WriteLine(“Person count is : ” + result.Count);

             Console.ReadKey();

         }

         //模拟源数据

         static List<Person> GetList()

         {

             var personList = new List<Person>();

             var person1 = new Person(1,"Leslie",29);

             personList.Add(person1);

             ........

             return personList;

         }

         //查询条件

         static bool Match(Person person)

         {

             return person.Age <= 30;

         }

     }

     public class Person

     {

         public Person(int id, string name, int age)

         {

             ID = id;

             Name = name;

             Age = age;

         }

         public int ID

         { get; set; }

         public string Name

         { get; set; }

         public int Age

         { get; set; }

     }


1
 

Action<T> 的使用方式与 Predicate<T> 相似,不同之处在于 Predicate<T> 返回值为 bool ,  Action<T> 的返回值为 void。 Action 支持0~16个参数,可以按需求任意使用。

public delegate void Action()

public delegate void Action<T1>(T1 obj1) public delegate void Action<T1,T2> (T1 obj1, T2 obj2) public delegate void Action<T1,T2,T3> (T1 obj1, T2 obj2,T3 obj3) ............ public delegate void Action<T1,T2,T3,......,T16> (T1 obj1, T2 obj2,T3 obj3,......,T16 obj16)


1

2

3

4

5

6

7

8

9

10

11

static void Main(string[] args)

         {

             Action<string> action=ShowMessage;

             action("Hello World");

             Console.ReadKey();

         }

         static void ShowMessage(string message)

         {

             MessageBox.Show(message);

         }


1
 

委托 Func 与 Action 相似,同样支持 0~16 个参数,不同之处在于Func 必须具有返回值

public delegate TResult Func<TResult>()

public delegate TResult Func<T1,TResult>(T1 obj1)

public delegate TResult Func<T1,T2,TResult>(T1 obj1,T2 obj2)

public delegate TResult Func<T1,T2,T3,TResult>(T1 obj1,T2 obj2,T3 obj3)

............

public delegate TResult Func<T1,T2,T3,......,T16,TResult>(T1 obj1,T2 obj2,T3 obj3,......,T16 obj16)


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

static void Main(string[] args)

         {

             Func<double, bool, double> func = Account;

             double result=func(1000, true);

             Console.WriteLine("Result is : "+result);

             Console.ReadKey();

         }

         static double Account(double a,bool condition)

         {

             if (condition)

                 return a * 1.5;

             else

                 return a * 2;

         }

3.2 lambda表达式

Lambda 的表达式的编写格式如下:

x=> x * 1.5

当中 “ => ” 是 Lambda 表达式的操作符,在左边用作定义一个参数列表,右边可以操作这些参数。

例子一, 先把 int x 设置 1000,通过 Action 把表达式定义为 x=x+500 ,最后通过 Invoke 激发委托。

static void Main(string[] args)          {              int x = 1000;              Action action = () => x = x + 500;              action.Invoke();              Console.WriteLine("Result is : " + x);              Console.ReadKey();          }

例子二,通过 Action<int> 把表达式定义 x=x+500, 到最后输入参数1000,得到的结果与例子一相同。 注意,此处Lambda表达式定义的操作使用 { } 括弧包括在一起,里面可以包含一系列的操作。

static void Main(string[] args)          {              Action<int> action = (x) =>              {                  x = x + 500;                  Console.WriteLine("Result is : " + x);              };              action.Invoke(1000);              Console.ReadKey();          }

时间: 2024-11-10 00:12:19

C# 委托、事件,lamda表达式的相关文章

委托、Lambda表达式、事件系列03,从委托到Lamda表达式

在"委托.Lambda表达式.事件系列02,什么时候该用委托"一文中,使用委托让代码简洁了不少. namespace ConsoleApplication2 { internal delegate bool MyCalculateDelegate(int val); class Program { static void Main(string[] args) { IEnumerable<int> source = new List<int>(){2, 3, 4

委托、泛型委托、多播委托 和 lamda表达式

委托基本概念:可以把一个方法作为一个参数传给另一个方法 声明:  方法前加 delegate  关键字 列子: using System; using System.Collections; using System.Collections.Generic; namespace Dome { class dom { static void Main(string[] args) { string[] sname = { "Abc", "dFg", "HjK

委托、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表达式、事件系列04,委托链是怎样形成的, 多播委托

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

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

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

[.net 面向对象程序设计进阶] (5) Lamda表达式(一) 创建委托

[.net 面向对象程序设计进阶] (5) Lamda表达式(一)  创建委托 本节导读: 通过学习Lambda表达式,学会创建委托和表达式目录树,深入了解Lambda的特性,让你的代码变的更加清晰.简洁.高效. 读前必备: 本节学习前,需要掌握以下知识: A.泛型        (请参考[.net 面向对象编程基础]  (18) 泛型) B.Linq基础 (请参照[.net 面向对象编程基础] (19) LINQ基础) C.Linq使用  (请参照[.net 面向对象编程基础]  (20) L

C#中委托,匿名函数,lamda表达式复习

一.委托 1.就给类比较,类用class声明,委托用delegate声明. 2.委托要指向一个真正的方法. 3.委托的签名,要和指向的方法一样. //1.声明一个委托 public delegate void DelSayHi(string name); public static void SayHiChinese(string name) { Console.WriteLine("你好?" + name); } public static void SayHiEnglish(str

C#基础加强篇—委托、Lambda表达式和事件(下)

3.事件 事件作为C#中的一种类型,为类和类的实例定义发出通知的能力,从而将事件和可执行代码捆绑在了一起.事件是对象发送的消息,以发信号通知操作的发生.操作可能是由用户交互引起的,也可能是由某些其他的程序逻辑触发的.引发(触发)事件的对象叫做事件发送方,捕获事件并对其做出响应的对象叫做事件接收方. C#事件是按“发布——预定”的方式工作.先在一个类中公布事件,然后就可以在任意数量的类中对事件预定.在事件通信中,事件发送方类并不知道哪个对象或方法将接收到(处理)它所引发的事件.所需要的是在源和接收

委托、Lambda表达式、事件系列02,什么时候该用委托

假设要找出整型集合中小于5的数. static void Main(string[] args) { IEnumerable<int> source = new List<int>(){2, 3, 4, 5, 6, 7, 8, 9,10, 11}; var result = GetNumbersLessThanFive(source); foreach (int n in result) { Console.WriteLine(n); } } static IEnumerable&