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

http://www.cnblogs.com/android-blogs/p/5851002.html


注:看红字部分

1. 委托Delegate

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

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

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

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

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

image

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();
              }
          }

输出

image

 

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来调用该委托所绑定的方法,而产生我们不需要的结果。

image

 

当然我们可以将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 方法。

image

 

事件能通过+=和-=两个方式注册或者注销对其处理的方法,使用+=与-=操作符的时候,系统会自动调用对应的 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();
         }

 

失败是什么?没有什么,只是更走近成功一步;成功是什么?就是走过了所有通向失败的路,只剩下一条路,那就是成功的路。

### C# Lambda 表达式简介 Lambda 表达式是一种简洁的方式定义内联函数,通常用于创建委托表达式树类型。这种表达式能够显著减少代码量并提高可读性[^3]。 #### 定义与基本结构 Lambda 表达式的典型形式由两部分组成:左侧为输入参数列表(如果存在),右侧则是执行逻辑,二者通过 `=>` 符号分隔开。对于无参的情况,则只需一对空括号即可表示[^4]。 ```csharp // 无参数的简单例子 Action simpleAction = () => Console.WriteLine("这是最简单的Lambda"); simpleAction(); ``` 当有单个参数时,甚至可以省略掉圆括号: ```csharp Func<int, int> square = x => x * x; Console.WriteLine(square(5)); // 输出25 ``` 多于一个参数的情形下则仍需保留括号,并且多个参数间要用逗号分开: ```csharp Func<int, int, int> addNumbers = (num1, num2) => num1 + num2; Console.WriteLine(addNumbers(7, 8)); // 输出15 ``` #### 集合操作中的应用 除了上述的基础用法外,在处理集合数据时,Lambda 表达式也极为有用。许多LINQ查询方法如 `Where`, `Select`, `OrderBy`, 和 `GroupBy` 等都可以接收Lambda作为筛选条件或是转换规则[^2]。 下面展示如何利用 `Where` 方法配合Lambda来过滤数组内的偶数项: ```csharp int[] numbers = new int[] { 1, 2, 3, 4, 5 }; var evenNumbers = numbers.Where(n => n % 2 == 0); foreach(var number in evenNumbers){ Console.Write($"{number} "); } // 输出 "2 4" ``` 同样地,也可以借助 `Select` 来实现投影变换——即改变原有对象的形式而不影响其本身: ```csharp string[] names = {"Alice", "Bob", "Charlie"}; var nameLengths = names.Select(name => $"Name '{name}' has length of {name.Length}"); foreach(var info in nameLengths){ Console.WriteLine(info); } /* 输出如下: Name 'Alice' has length of 5 Name 'Bob' has length of 3 Name 'Charlie' has length of 7 */ ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值