第8章 委托、Lamdba表达式和事件
本章内容:
- 委托
- Lambda表达式
- 事件
8.1.3 简单的委托示例
首先定义一个类MathOperations,它有两个静态方法,对double类型的值执行两个操作。
public class MathOperations
{
public static double MultiplyByTwo(double value)
{
;
}
public static double Square(double value)
{
return value*value;
}
}
下面使用自定义的委托调用这两个方法
internal delegate double DoubleOp(double x);
private static void Main()
{
DoubleOp[] operations =
{
MathOperations.MultiplyByTwo,
MathOperations.Square
};
; i < operations.Length; i++)
{
Console.WriteLine("Using operations[{0}]", i);
ProcessAndDisplayNumber(operations[i], 2.0);
ProcessAndDisplayNumber(operations[i], 7.94);
ProcessAndDisplayNumber(operations[i], 1.141);
Console.WriteLine();
}
Console.ReadLine();}
private static void ProcessAndDisplayNumber(DoubleOp action, double value)
{
var result = action(value);
Console.WriteLine("Value is {0},result of operation is {1}", value, result);
}
运行这个示例,得到如下所示的代码:

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>委托,如下所示:
Func<double,double> [] operations =
{
MathOperations.MultiplyByTwo,
MathOperations.Square
};
然后修改ProcessAndDisplayNumber方法的第一个参数类型,如下所示:
private static void ProcessAndDisplayNumber(Func<double,double> action, double value)
{
var result = action(value);
Console.WriteLine("Value is {0},result of operation is {1}", value, result);
}
8.1.5 BubbleSorter示例
现在有一个类BubbleSorter,它有一个静态方法Sort(),这个方法只有一个int数组类型的参数,用来把数组按照升序重新排列。
假如传递给它的是int数组:{0,5,6,2,1},则方法执行完毕后int数组为:{0,1,2,5,6}。
这就是经典的冒泡排序,是一种简单的排序方法。它适合于一小组数字。下面是代码:
public class BubbleSorter
{
public static void Sort(int[] array)
{
bool swapped;
do
{
swapped = false;
; i < array.Length - ; i++)
{
])
{
continue;
}
var temp = array[i];
array[i] = array[i + ];
array[i + ] = temp;
swapped = true;
}
} while (swapped);
}
}
它非常适合于int,但是我们希望Sort()方法能给任何对象排序,这时上面的比较方法就有问题了,为了实现这个功能,我们要使用泛型方法和
委托。如下所示:
public class BubbleSorter
{
public static void Sort<T>(IList<T> sortArray, Func<T, T, bool> comparison)
{
bool swapped;
do
{
swapped = false;
; i < sortArray.Count - ; i++)
{
], sortArray[i]))
{
continue;
}
T temp = sortArray[i];
sortArray[i] = sortArray[i + ];
sortArray[i + ] = temp;
swapped = true;
}
} while (swapped);
}
}
为了使用这个类,我们需要定义一个员工类,根据他们的薪水进行排序。
public class Employee
{
public string Name { get; private set; }
public decimal Salary { get; private set; }
public Employee(string name, decimal salary)
{
Name = name;
Salary = salary;
}
public override string ToString()
{
return string.Format("{0},{1:C}", Name, Salary);
}
public static bool CompareSalary(Employee e1, Employee e2)
{
return e1.Salary < e2.Salary;
}}
下面编写客户端代码,完成排序功能:
private static void Main()
{
Employee[] employees =
{
),
),
),
new Employee("Lucy", 1000000.38M),
)
};
BubbleSorter.Sort(employees, Employee.CompareSalary);
foreach (var employee in employees)
{
Console.WriteLine(employee);
}
Console.ReadLine();
}
运行这段代码,正确显示按照薪水排列的Employee,如下图所示:

8.1.6 多播委托
前面使用的每个委托都只包含一个方法调用,委托也可以包含多个方法,这种委托称为多播委托。如果调用多播委托,
就可以按顺序连续调用多个方法。 多播委托可以识别运算符"+","+=","-"和"-="。
为了说明多播委托的用法,我们修改之前示例中的部分代码,如下所示:
public class MathOperations
{
public static void MultiplyByTwo(double value)
{
;
Console.WriteLine("{0} Multiply by 2 equlas {1}", value, result);
}
public static void Square(double value)
{
var result = value*value;
Console.WriteLine("{0} squaring equlas {1}", value, result);
}
}
private static void Main()
{
Action<double> operations = MathOperations.MultiplyByTwo;
operations += MathOperations.Square;
ProcessAndDispalyNumber(operations, 2.0);
ProcessAndDispalyNumber(operations, 7.94);
ProcessAndDispalyNumber(operations, 1.414);
Console.ReadLine();
}
public static void ProcessAndDispalyNumber(Action<double> action, double value)
{
Console.WriteLine();
Console.WriteLine("ProcessAndDispalyNumber called with value ={0}", value);
action(value);
}
运行代码,得到如下图所示的代码:

使用多播委托应注意,如果通过委托调用的其中一个方法抛出一个异常,整个迭代就会停止,如下面的示例所示:
private static void Main()
{
Action d1 = One;
d1 += Two;
try
{
d1();
}
catch (Exception)
{
Console.WriteLine("Exception caught");
}
// 运行结果
// One
// Exception caught
Console.ReadLine();
}
private static void One()
{
Console.WriteLine("One");
throw new Exception("Error in one");
}
private static void Two()
{
Console.WriteLine("Two");
}
从运行结果来看,我们看出委托只调用了第一个方法,因为第一个方法抛出了一个异常,所以委托的迭代会停止,不再调用Two()方法。
在这种情况下,为了避免这个问题,应自己迭代方法列表,为此我们修改代码如下:
private static void Main()
{
Action d1 = One;
d1 += Two;
Delegate[] delegates = d1.GetInvocationList();
foreach (Action d in delegates)
{
try
{
d();
}
catch (Exception)
{
Console.WriteLine("Exception caught");
}
}
// 运行结果
// One
// Exception caught
// Two
Console.ReadLine();
}
从运行结果我们可以看到,在捕获了异常后,将继续迭代下一个方法。
8.1.7 匿名方法
到目前为止,要想使委托工作,方法必须已经存在(即委托是用它将调用的方法的相同签名定义的)。
但还有另外一种使用委托的方式:即通过匿名方法。
下面通过一个简单的例子说明如何使用匿名方法:
private static void Main()
{
var str = ",Middle part,";
Func<string, string> anonDel = delegate(string param)
{
param += str;
param += "End part";
return param;
};
Console.WriteLine(anonDel("Strat Part"));
// 输出结果
// Strat Part,Middle part,End part
Console.ReadLine();
}
匿名方法的优点:减少了要编写的代码,不必定义仅由委托使用的方法,有助于降低代码的复杂性。
从C#3.0开始,可以使用Lambda表达式替代匿名方法。
8.2 Lambda 表达式
自C#3.0开始,可以使用Lambda表达式把实现代码赋予委托,只要有委托参数类型的地方,就可以使用Lambda表达式。
下面将前面使用匿名方法的例子改为使用Lambda表达式:
private static void Main()
{
var str = ",Middle part,";
Func<string, string> lambda = param =>
{
param += str;
param += "End part";
return param;
};
Console.WriteLine(lambda("Strat Part"));
// 输出结果
// Strat Part,Middle part,End part
Console.ReadLine();
}
从上面的代码可以看出,Lambda运算符"=>"的左边列出了需要的参数,右边定义了赋予lambda变量的方法的实现代码。
8.2.1 参数
Lambda表达式有几种定义参数的方式,如果只有一个参数,只写出参数名就够了,如下所示:
Func<string, string> oneParam = s => string.Format("change uppercase {0}",s.ToUpper());
Console.WriteLine(oneParam("test"));
// 输出结果
// change uppercase TEST
如果委托使用多个参数,就把参数放在花括号中。如下所示:
Func<double, double, double> twoParams = (x, y) => x*y; Console.WriteLine(twoParams(, )); // 输出结果
也可以在花括号中给变量名添加参数类型:
Func<double, double, double> twoParams = (double x, double y) => x*y;
8.2.2 多行代码
如果Lambda表达式只有一条语句,在方法块内就不需要花括号和return语句,因为编译器会添加一条隐式的return语句。
Func<double, double> square = x => x*x;
添加花括号、return语句和分号是完全合法的,通常这比不添加这些符号更容易阅读:
Func<double, double> square = x =>
{
return x*x;
};
如果在Lambda表达式的实现代码中需要多条语句,就必须添加花括号和return语句:
var str = ",Middle part,";
Func<string, string> lambda = param =>
{
param += str;
param += "End part";
return param;
};
第8章 委托、Lamdba表达式和事件的更多相关文章
- C#高级编程9-第8章 委托、lamdba表达式和事件
委托.lamdba表达式和事件 1.引用方法 函数指针是一个指向内存位置的指针,不是类型安全的.无法判断实际指向.参数和返回类型也无从知晓..NET委托是类型安全的.定义了返回类型和参数类型,不仅包含 ...
- C#编程 委托 Lambda表达式和事件
委托 如果我们要把方法当做参数来传递的话,就要用到委托.简单来说委托是一个类型,这个类型可以赋值一个方法的引用. 声明委托 在C#中使用一个类分两个阶段,首选定义这个类,告诉编译器这个类由什么字段和方 ...
- C#学习笔记三(委托·lambda表达式和事件,字符串和正则表达式,集合,特殊的集合)
委托和事件的区别 序号 区别 委托 事件 1 是否可以使用=来赋值 是 否 2 是否可以在类外部进行调用 是 否 3 是否是一个类型 是 否,事件修饰的是一个对象 public delegate vo ...
- C#高级编程(第9版) 第08章 委托、lambda表达式和事件 笔记
本章代码分为以下几个主要的示例文件: 1. 简单委托 2. 冒泡排序 3. lambda表达式 4. 事件示例 5. 弱事件 引用方法 委托是寻址方法的.NET版本.在C++中函数 ...
- 第八章 委托,lamdbda 表达式和事件
第八章 委托,lamdbda 表达式和事件 委托是寻址方式的.net版本. 委托是类型安全的类,它定义了返回类型和参数的类型.委托类不仅包含方法的应用,也可以包含对多个方法的引用. 在 C++中,函数 ...
- 《C#从现象到本质》读书笔记(六)第8章委托和事件
<C#从现象到本质>读书笔记(六)第二部分 C#特性 第8章委托和事件 从这一部分开始,知识点就相对少了,重要的是代码练习.奈何太高深的代码平常不怎么用,这些特性也不是经常写代码的. 委托 ...
- C# 委托、lambda表达式和事件
什么是委托?委托就是持有一个或多个方法的对象,并且该对象可以执行,可以传递. using System; using System.Collections.Generic; using System. ...
- 委托、Lambda表达式、事件系列07,使用EventHandler委托
谈到事件注册,EventHandler是最常用的. EventHandler是一个委托,接收2个形参.sender是指事件的发起者,e代表事件参数. □ 使用EventHandler实现猜拳游戏 使用 ...
- 委托、Lambda表达式、事件系列06,使用Action实现观察者模式,体验委托和事件的区别
在"实现观察者模式(Observer Pattern)的2种方式"中,曾经通过接口的方式.委托与事件的方式实现过观察者模式.本篇体验使用Action实现此模式,并从中体验委托与事件 ...
随机推荐
- RDBMS vs. NoSQL 合作还是竞争
欢迎转载,转载请注明出处,徽沪一郎. 由于近期手头的工作和数据库的选型相关,纠结于是否使用一款NoSQL数据库来替换已有的MySQL数据库.在这个过程中随着学习研究的深入,对于二者的异同有了一些初步的 ...
- error while loading shared libraries: xxx.so.x" 错误的原因和解决办法
今天在执行一个protobuf程序时,提示error while loading shared libraries: libprotobuf.so.8: cannot open shared obje ...
- phantomjs 安装使用
PhantomJS 是一个基于 WebKit 的服务器端 JavaScript API.它全面支持web而不需浏览器支持,其快速,原生支持各种Web标准: DOM 处理, CSS 选择器, JSON, ...
- HOSTS文件详解【win|mac】
hosts文件是一个用于储存计算机网络中各节点信息的计算机文件.这个文件负责将主机名映射到相应的IP地址. hosts文件通常用于补充或取代网络中DNS的功能.和DNS不同的是,计算机的使用者可以直接 ...
- TCPdump抓包命令详解--摘
http://blog.csdn.net/s_k_yliu/article/details/6665673/ http://starsliao.blog.163.com/blog/static/890 ...
- cascading rollback 级联回滚
Computer Science An Overview _J. Glenn Brookshear _11th Edition To emphasize the delicate nature of ...
- Java中的匿名对象
匿名对象就是没有明确给出名字的对象.一般匿名对象只使用一次,而且匿名对象只在堆内存中开辟空间,而不存在栈内存的引用. 一个普通的常量字符串就可以表示一个匿名String对象. 比如可以 int len ...
- html轮播效果的实现
要实现如下图的效果 点击可以选择图片:不点击的时候自动轮播:并且点击完后再次自动轮播. 思路:如同在房子里透过窗子看路过的火车一样,窗子是不动的,但火车是陆续经过窗子的,所以透过窗子可以看到依次看完所 ...
- 单选按钮控件(Ridio Button)的使用
VC学习笔记5:单选按钮控件(Ridio Button)的使用 一.对单选按钮进行分组: 每组的第一个单选按钮设置属性:Group,Tabstop,Auto;其余按钮设置属性Tabstop,Auto. ...
- Delphi Dll示例
//MyInt.pas unit MyInt; interface {$IFNDEF MYLIB} function MyAdd(a,b:integer):integer ;stdcall; {$EN ...