本章内容:

  •  委托
  •    Lambda表达式
  •  事件

8.1.3   简单的委托示例

首先定义一个类MathOperations,它有两个静态方法,对double类型的值执行两个操作。  

  1. public class MathOperations
  2. {
  3. public static double MultiplyByTwo(double value)
  4. {
  5. ;
  6. }
  7.  
  8. public static double Square(double value)
  9. {
  10. return value*value;
  11. }
  12. }

下面使用自定义的委托调用这两个方法

  1. internal delegate double DoubleOp(double x);
  1. private static void Main()
  2. {
  3. DoubleOp[] operations =
  4. {
  5. MathOperations.MultiplyByTwo,
  6. MathOperations.Square
  7.    };
  8.    ; i < operations.Length; i++)
  9. {
  10.   Console.WriteLine("Using operations[{0}]", i);
  11.   ProcessAndDisplayNumber(operations[i], 2.0);
  12.   ProcessAndDisplayNumber(operations[i], 7.94);
  13.   ProcessAndDisplayNumber(operations[i], 1.141);
  14.   Console.WriteLine();
  15. }
  16.   Console.ReadLine();}
  17.  
  18. private static void ProcessAndDisplayNumber(DoubleOp action, double value)
  19. {
  20. var result = action(value);
  21. Console.WriteLine("Value is {0},result of operation is {1}", value, result);
  22. }

运行这个示例,得到如下所示的代码:

8.1.4  Action<T>和Func<T>委托

  除了自定义的委托类型外,我们还可以使用Action<T>委托和Func<T>委托。

  泛型Action<T>委托表示引用一个void返回类型的方法,因为这个委托类存在不同的变体,所以可以传递至多16种不同的参数类型,

没有泛型参数的Action类可调用没有参数的方法。

  Func<T>允许调用带返回类型的方法,与Action<T>类似,Func<T>也定义了不同的变体,至多也可以传递16个参数类型和一个返回类型。

  Func<out TResult>委托类型可以调用带返回类型且无参数的方法。

  

  理解了这两个委托后,我们就可以将上一节种定义的DoubleOp委托删除,改为Func<T>委托,如下所示:  

  1. Func<double,double> [] operations =
  2. {
  3. MathOperations.MultiplyByTwo,
  4. MathOperations.Square
  5. };

  然后修改ProcessAndDisplayNumber方法的第一个参数类型,如下所示:

  1. private static void ProcessAndDisplayNumber(Func<double,double> action, double value)
  2. {
  3. var result = action(value);
  4. Console.WriteLine("Value is {0},result of operation is {1}", value, result);
  5. }

8.1.5  BubbleSorter示例

  现在有一个类BubbleSorter,它有一个静态方法Sort(),这个方法只有一个int数组类型的参数,用来把数组按照升序重新排列。

假如传递给它的是int数组:{0,5,6,2,1},则方法执行完毕后int数组为:{0,1,2,5,6}。

  这就是经典的冒泡排序,是一种简单的排序方法。它适合于一小组数字。下面是代码:

  1. public class BubbleSorter
  2. {
  3. public static void Sort(int[] array)
  4. {
  5. bool swapped;
  6. do
  7. {
  8. swapped = false;
  9. ; i < array.Length - ; i++)
  10. {
  11. ])
  12. {
  13. continue;
  14. }
  15. var temp = array[i];
  16. array[i] = array[i + ];
  17. array[i + ] = temp;
  18. swapped = true;
  19. }
  20. } while (swapped);
  21. }
  22. }

  它非常适合于int,但是我们希望Sort()方法能给任何对象排序,这时上面的比较方法就有问题了,为了实现这个功能,我们要使用泛型方法和

委托。如下所示:

  

  1. public class BubbleSorter
  2. {
  3. public static void Sort<T>(IList<T> sortArray, Func<T, T, bool> comparison)
  4. {
  5. bool swapped;
  6. do
  7. {
  8. swapped = false;
  9. ; i < sortArray.Count - ; i++)
  10. {
  11. ], sortArray[i]))
  12. {
  13. continue;
  14. }
  15. T temp = sortArray[i];
  16. sortArray[i] = sortArray[i + ];
  17. sortArray[i + ] = temp;
  18. swapped = true;
  19. }
  20. } while (swapped);
  21. }
  22. }

为了使用这个类,我们需要定义一个员工类,根据他们的薪水进行排序。

  1. public class Employee
  2. {
  3. public string Name { get; private set; }
  4.  
  5. public decimal Salary { get; private set; }
  6.  
  7. public Employee(string name, decimal salary)
  8. {
  9. Name = name;
  10. Salary = salary;
  11. }
  12.  
  13. public override string ToString()
  14. {
  15. return string.Format("{0},{1:C}", Name, Salary);
  16. }
  17.  
  18. public static bool CompareSalary(Employee e1, Employee e2)
  19. {
  20. return e1.Salary < e2.Salary;
  21. }}

下面编写客户端代码,完成排序功能:

  1. private static void Main()
  2. {
  3. Employee[] employees =
  4. {
  5. ),
  6. ),
  7. ),
  8. new Employee("Lucy", 1000000.38M),
  9. )
  10. };
  11. BubbleSorter.Sort(employees, Employee.CompareSalary);
  12.  
  13. foreach (var employee in employees)
  14. {
  15. Console.WriteLine(employee);
  16. }
  17.  
  18. Console.ReadLine();
  19. }

运行这段代码,正确显示按照薪水排列的Employee,如下图所示:

8.1.6  多播委托

  前面使用的每个委托都只包含一个方法调用,委托也可以包含多个方法,这种委托称为多播委托。如果调用多播委托,

就可以按顺序连续调用多个方法。 多播委托可以识别运算符"+","+=","-"和"-="。

  为了说明多播委托的用法,我们修改之前示例中的部分代码,如下所示:

  1. public class MathOperations
  2. {
  3. public static void MultiplyByTwo(double value)
  4. {
  5. ;
  6. Console.WriteLine("{0} Multiply by 2 equlas {1}", value, result);
  7. }
  8.  
  9. public static void Square(double value)
  10. {
  11. var result = value*value;
  12. Console.WriteLine("{0} squaring equlas {1}", value, result);
  13. }
  14. }
  1. private static void Main()
  2. {
  3. Action<double> operations = MathOperations.MultiplyByTwo;
  4. operations += MathOperations.Square;
  5.  
  6. ProcessAndDispalyNumber(operations, 2.0);
  7. ProcessAndDispalyNumber(operations, 7.94);
  8. ProcessAndDispalyNumber(operations, 1.414);
  9.  
  10. Console.ReadLine();
  11. }
  12.  
  13. public static void ProcessAndDispalyNumber(Action<double> action, double value)
  14. {
  15. Console.WriteLine();
  16. Console.WriteLine("ProcessAndDispalyNumber called with value ={0}", value);
  17. action(value);
  18. }

运行代码,得到如下图所示的代码:

使用多播委托应注意,如果通过委托调用的其中一个方法抛出一个异常,整个迭代就会停止,如下面的示例所示:

  1. private static void Main()
  2. {
  3. Action d1 = One;
  4. d1 += Two;
  5.  
  6. try
  7. {
  8. d1();
  9. }
  10. catch (Exception)
  11. {
  12. Console.WriteLine("Exception caught");
  13. }
  14.  
  15. // 运行结果
  16. // One
  17. // Exception caught
  18.  
  19. Console.ReadLine();
  20. }
  21.  
  22. private static void One()
  23. {
  24. Console.WriteLine("One");
  25. throw new Exception("Error in one");
  26. }
  27.  
  28. private static void Two()
  29. {
  30. Console.WriteLine("Two");
  31. }

从运行结果来看,我们看出委托只调用了第一个方法,因为第一个方法抛出了一个异常,所以委托的迭代会停止,不再调用Two()方法。

在这种情况下,为了避免这个问题,应自己迭代方法列表,为此我们修改代码如下:

  1. private static void Main()
  2. {
  3. Action d1 = One;
  4. d1 += Two;
  5.  
  6. Delegate[] delegates = d1.GetInvocationList();
  7. foreach (Action d in delegates)
  8. {
  9. try
  10. {
  11. d();
  12. }
  13. catch (Exception)
  14. {
  15. Console.WriteLine("Exception caught");
  16. }
  17. }
  18.  
  19. // 运行结果
  20. // One
  21. // Exception caught
  22. // Two
  23.  
  24. Console.ReadLine();
  25. }

从运行结果我们可以看到,在捕获了异常后,将继续迭代下一个方法。

8.1.7   匿名方法

到目前为止,要想使委托工作,方法必须已经存在(即委托是用它将调用的方法的相同签名定义的)。

但还有另外一种使用委托的方式:即通过匿名方法。

下面通过一个简单的例子说明如何使用匿名方法:

  1. private static void Main()
  2. {
  3. var str = ",Middle part,";
  4. Func<string, string> anonDel = delegate(string param)
  5.   {
  6.   param += str;
  7.   param += "End part";
  8.   return param;
  9.   };
  10. Console.WriteLine(anonDel("Strat Part"));
  11.  
  12. // 输出结果
  13. // Strat Part,Middle part,End part
  14.  
  15. Console.ReadLine();
  16. }

匿名方法的优点:减少了要编写的代码,不必定义仅由委托使用的方法,有助于降低代码的复杂性。

从C#3.0开始,可以使用Lambda表达式替代匿名方法。

8.2   Lambda 表达式

自C#3.0开始,可以使用Lambda表达式把实现代码赋予委托,只要有委托参数类型的地方,就可以使用Lambda表达式。

下面将前面使用匿名方法的例子改为使用Lambda表达式:

  1. private static void Main()
  2. {
  3. var str = ",Middle part,";
  4. Func<string, string> lambda = param =>
  5. {
  6. param += str;
  7. param += "End part";
  8. return param;
  9. };
  10. Console.WriteLine(lambda("Strat Part"));
  11.  
  12. // 输出结果
  13. // Strat Part,Middle part,End part
  14.  
  15. Console.ReadLine();
  16. }

从上面的代码可以看出,Lambda运算符"=>"的左边列出了需要的参数,右边定义了赋予lambda变量的方法的实现代码。  

8.2.1   参数

Lambda表达式有几种定义参数的方式,如果只有一个参数,只写出参数名就够了,如下所示:

  1. Func<string, string> oneParam = s => string.Format("change uppercase {0}",s.ToUpper());
  2. Console.WriteLine(oneParam("test"));
  3.  
  4. // 输出结果
  5. // change uppercase TEST

如果委托使用多个参数,就把参数放在花括号中。如下所示:

  1. Func<double, double, double> twoParams = (x, y) => x*y;
  2. Console.WriteLine(twoParams(, ));
  3.  
  4. // 输出结果

也可以在花括号中给变量名添加参数类型:

  1. Func<double, double, double> twoParams = (double x, double y) => x*y;

8.2.2  多行代码

如果Lambda表达式只有一条语句,在方法块内就不需要花括号和return语句,因为编译器会添加一条隐式的return语句。

  1. Func<double, double> square = x => x*x;

添加花括号、return语句和分号是完全合法的,通常这比不添加这些符号更容易阅读:

  1. Func<double, double> square = x =>
  2. {
  3. return x*x;
  4. };

如果在Lambda表达式的实现代码中需要多条语句,就必须添加花括号和return语句:

  1. var str = ",Middle part,";
  2. Func<string, string> lambda = param =>
  3. {
  4. param += str;
  5. param += "End part";
  6. return param;
  7. };

第8章 委托、Lamdba表达式和事件的更多相关文章

  1. C#高级编程9-第8章 委托、lamdba表达式和事件

    委托.lamdba表达式和事件 1.引用方法 函数指针是一个指向内存位置的指针,不是类型安全的.无法判断实际指向.参数和返回类型也无从知晓..NET委托是类型安全的.定义了返回类型和参数类型,不仅包含 ...

  2. C#编程 委托 Lambda表达式和事件

    委托 如果我们要把方法当做参数来传递的话,就要用到委托.简单来说委托是一个类型,这个类型可以赋值一个方法的引用. 声明委托 在C#中使用一个类分两个阶段,首选定义这个类,告诉编译器这个类由什么字段和方 ...

  3. C#学习笔记三(委托·lambda表达式和事件,字符串和正则表达式,集合,特殊的集合)

    委托和事件的区别 序号 区别 委托 事件 1 是否可以使用=来赋值 是 否 2 是否可以在类外部进行调用 是 否 3 是否是一个类型 是 否,事件修饰的是一个对象 public delegate vo ...

  4. C#高级编程(第9版) 第08章 委托、lambda表达式和事件 笔记

          本章代码分为以下几个主要的示例文件: 1. 简单委托 2. 冒泡排序 3. lambda表达式 4. 事件示例 5. 弱事件     引用方法 委托是寻址方法的.NET版本.在C++中函数 ...

  5. 第八章 委托,lamdbda 表达式和事件

    第八章 委托,lamdbda 表达式和事件 委托是寻址方式的.net版本. 委托是类型安全的类,它定义了返回类型和参数的类型.委托类不仅包含方法的应用,也可以包含对多个方法的引用. 在 C++中,函数 ...

  6. 《C#从现象到本质》读书笔记(六)第8章委托和事件

    <C#从现象到本质>读书笔记(六)第二部分 C#特性 第8章委托和事件 从这一部分开始,知识点就相对少了,重要的是代码练习.奈何太高深的代码平常不怎么用,这些特性也不是经常写代码的. 委托 ...

  7. C# 委托、lambda表达式和事件

    什么是委托?委托就是持有一个或多个方法的对象,并且该对象可以执行,可以传递. using System; using System.Collections.Generic; using System. ...

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

    谈到事件注册,EventHandler是最常用的. EventHandler是一个委托,接收2个形参.sender是指事件的发起者,e代表事件参数. □ 使用EventHandler实现猜拳游戏 使用 ...

  9. 委托、Lambda表达式、事件系列06,使用Action实现观察者模式,体验委托和事件的区别

    在"实现观察者模式(Observer Pattern)的2种方式"中,曾经通过接口的方式.委托与事件的方式实现过观察者模式.本篇体验使用Action实现此模式,并从中体验委托与事件 ...

随机推荐

  1. 代理和block反向传值

    代理传值: // SendViewController.h #import <UIKit/UIKit.h> @protocol SendInFor <NSObject> -(v ...

  2. endsWith和startsWith同样效果其他形式的写法(2016.1.12)

    //判断以什么开始startWith str = "abcdef"; //用其他的形式写的startsWith if(str.indexOf("abc")==0 ...

  3. Socket通信原理探讨(C++为例) good

    http://www.cnblogs.com/xufeiyang/articles/4878096.html http://www.cnblogs.com/xufeiyang/articles/453 ...

  4. CSS控制"标题前增加小图标或编号"

    ---题目前加图片--- p:before { content:url(xxx/xx.png); }//所有p的最前都有一个图标 p.a:after { content:url(xxx/xx.png) ...

  5. JavasScript判断输入框不为空

    <form name="form1" method="POST" action="add.php"> <table wid ...

  6. .SQL Server中 image类型数据的比较

    原文:.SQL Server中 image类型数据的比较 在SQL Server中如果你对text.ntext或者image数据类型的数据进行比较.将会提示:不能比较或排序 text.ntext 和 ...

  7. windows SVN搭建

    Subversion是优秀的版本控制工具,其具体的的优点和详细介绍,这里就不再多说. 首先来下载和搭建SVN服务器. 现在Subversion已经迁移到apache网站上了,下载地址: http:// ...

  8. Round and Round We Go

    http://acm.hdu.edu.cn/showproblem.php?pid=1313 考查大整数与小整数相乘 #include<iostream> #include<cstd ...

  9. Marshal.SecureStringToBSTR

    Marshal.StringToBSTR 方法 命名空间:System.Runtime.InteropServices程序集:mscorlib(在 mscorlib.dll 中) // 使用一个Int ...

  10. Java学习-016-CSV 文件读取实例源代码

    上文(CSV文件写入)讲述了日常自动化测试过程中将测试数据写入 CSV 文件的源码,此文主要讲述如何从 CSV 文件获取测试过程中所需的参数化数据.敬请各位小主参阅,若有不足之处,敬请大神指正,不胜感 ...