思考并回答以下问题:
- 不需要每次传递一个方法时都定义新接口怎么理解?
- 静态类有什么特性?为什么要让一个类为静态?
- switch怎么改成委托?
- 为什么要使用Lambda表达式?
- Action\
怎么理解?Func代表什么委托?
本章涵盖:
可以创建类来封装数据以及对数据操作。随着创建的类越来越多,你会发现类和类的关系存在着一些常见的模式。一种常见的模式是向方法传递对象,该方法再调用对象的一个方法。例如,向方法传递一个IComparer\
Lambda表达式是从C#3.0开始加入的。C#2.0支持用匿名方法(anonymous method)这样一种不太优雅的语法来创建自定义委托。C#2.0之后的每个C#版本都支持匿名方法以保持向后兼容,但新写的代码应该弃用它,代之以Lambda表达式。本章将通过“高级主题”来描述如何使用匿名方法。只有要使用遗留的C#2.0代码时才需要了解这些主题,否则完全可以忽略这些补充内容。
委托概述
长期以来,经验丰富的C和C++程序员利用“函数指针”将对方法的引用作为实参传给另一个方法。C#使用委托提供相同的功能。委托允许捕捉对方法的引用,并像传递其他对象那样传递这个引用,像调用其他方法那样调用这个被捕捉的方法。来看看下面的例子。
背景
虽然效率不高,但冒泡排序或许是最简单的排序例程了。代码清单1展示了Bubblesort()方法。
代码清单1 Bubblesort()方法
1 | using System; |
该方法对整数数组执行升序排序。
为了能选择升级或降序来排序整数,有两个方案可选:一是复制上述代码,然后将大于操作符替换成小于操作符。但是复制这么多代码只是改变了一个操作符,这似乎不是一个好主意;二是传递一个附加参数,指出如何排序,如代码清单2所示。
代码清单2 Bubblesort()方法,升序或降序
1 | using System; |
然而,上述代码只是照顾到了两种可能的排序方式。假如想按字典顺序排序(即1, 10, 11, 12, 2, 20, …),或者按其他方式排序,sortType值以及对应的swich分支的数量很快就会变得非常“恐怖”。
委托数据类型
为了增强灵活性和减少重复代码,可以将比较方法作为参数传给Bubblesort()方法。为了能将方法作为参数传递,必须要有一个能够表示方法的数据类型。这个数据类型就是委托,因为它“委托”调用对象所引用的方法。代码清单3对Bubblesort()方法进行了修改,它现在能获取一个委托参数。在本例中,委托数据类型是ComparisonHandler。
代码清单3 带有委托参数的Bubblesort()方法
1 | class DelegateSample |
ComparisonHandler是委托类型,代表对两个整数进行比较的方法。在Bubblesort()方法中,可以使用comparisonMethod参数所引用的ComparisonHandler的实例来判断哪个整数更大。由于comparisonMethod代表一个方法,所以调用它的语法与直接调用方法的语法完全一致。在这个例子中,comparisonMethod委托获取两个整数参数,返回一个布尔值来指出第一个整数是否大于第二个。传方法进来要注意的是,ComparisonHandler委托是强类型的,它代表返回一个bool值,而且正好接受两个整数参数的方法。和其他方法调用一样,对委托的调用是强类型的。假如数据类型不匹配,C#编译器会报错。
委托类型的声明
前面描述了如何定义使用委托的方法,并介绍了如何将委托变量当作方法,从而简单地发出对委托的调用。然而,还必须学习如何声明委托类型。为了声明委托类型,要使用delegate关键字,后面跟着像是方法声明的东西。这个方法的签名是委托所引用的方法的签名。正常方法声明中方法名称的位置要替换成委托类型的名称。代码清单4展示了如何声明ComparisonHandler委托类型来要求两个整数并返回一个布尔值。
代码清单4 声明一个委托类型
1 | public delegate bool ComparisonHandler(int first, int second); |
就像类能嵌套在其他类中一样,委托也能嵌套在类中。假如委托声明出现在另一个类的内部,委托类型就会成为嵌套类型,如代码清单5所示。
代码清单5 声明嵌套的委托数据类型
1 | class DelegateSample |
在这个例子中,声明的委托数据类型是DelegateSample.ComparisonHandler,因为它被定义成DelegateSample中的嵌套类型。
委托的实例化
在使用委托来实现Bubblesort()方法的最后一步中,你将学习如何调用方法并传递委托实例-具体地说,传递ComparisonHandler类型的实例。为了实例化委托,需要一个和委托类型自身的签名匹配的方法。对于ComparisonHandler,这个方法应获取两个整数,并返回boo1值。方法名称无关紧要,但是方法的签名的剩余部分(参数和返回值)必须兼容委托的签名。代码清单12-6展示了与委托类型兼容的GreaterThan()方法。
代码清单12-6声明与ComparisonHandler兼容的方法
1 | public delegate bool ComparisonHandler (int first, int second); |
原来用的是一个方法,现在是多个方法1
2public static bool GreaterThan(int first, int second)।
return first > second;
定义好方法之后,就可以调用Bubblesort(),并提供由委托捕捉到的方法的名称作为实参,如代码清单12-7所示。
代码清单12-7 使用方法名作为实参
1 | public delegate bool ComparisonHandler (int first, int second). |
注意, ComparisonHandler委托是引用类型,但不必用new实例化它。从C# 2.0开始,从方法组(为方法命名的表达式)向委托类型的转换会自动创建一个新的委托对象。
高级主题: C#1.0中的委托实例化
在代码清单12-7中,调用Bubblesort()时传递所需方法的名称(GreaterThan )作为一个实参就可以实例化委托。C#的第一个版本要求使用如代码清单12-8所示的较复杂的语法来实例化委托。
代码清单8 C#1.0中将委托作为参数传递
1 | Bubblesort(items |
以后的版本支持上述两种语法。本书剩余的部分只使用更现代的、简洁的语法。高级主题:委托的内部机制
1委托实际是特殊的类]虽然C#标准没有确切规定类的层次结构应该是怎样的,但委托必须直接或间接地派生自System. Delegate。事实上, .NET中的委托类型总是 生自System.MulticastDelegate,后者又从System.Delegate派生,如图12-1所示。
反射图12-1 委托类型的对象模型
第一个属性属于(System.Reflection. MethodInfo)类型,它是第17章要讨论的主题。Methodinfo描述了特定方法的签名,包括方法名称、参数和返回类型除了Methodinfo,委托还需要一个对象实例,其中包含了要调用的方法。这正是第二个属性Target的作用。|在静态方法的情况下, Target对应于类型自身1至于MulticastDelegate类的作用,将在下一章详细描述。 target字段
注意,所有委托都是不可变的。委托一旦创建好就无法更改。如果变量包含了委托的引用,还想引用其他不同的方法,那就必须创建一个新委托,并把它指派给这个变量。虽然所有委托数据类型都是间接地从System. Delegate派生的,但C#编译器不允许声明直接或间接从System.Delegate或者System.MulticastDelegate派生的类。代码清单12-9的代码是无效的。
代码清单12-9 System.Delegate不能显式地作为基类1
2
3V ERROR: 'ComparisonHandLer. cannot
// innerit from special class 'System. Delegate"// public class ComparisonHandLer: System. DeLegate
//..
通过传递委托来指定排序方式显然要比本章开头的方式灵活得多。例如,为了改为按字母排序,只需添加一个附加的委托,在比较过程中将整数转换为字符串。代码清单12-10提供了实现按字母排序的完整代码,输出12-1展示了结果。
代码清单12-10使用其他与ComparisonHandler兼容的方法1
2
3
4
5
6
7
8
9
10
11using System;
class DelegateSample
public delegate bool comparisonHandler(int first, int second);public static void Bubblesorto
int[] items, ComparisonHandler comparisonMethod)
int i;int j;int temp;
for (1 = items.Length -1; 1 >= 0; 1--).
for (j=1;j<=i; j++)t
if (comparisonMethod(items [j-1], items [j)
temp = items[i-11;items[j-1] - items [3];items[i] = temp;
public static bool GreaterThan(int first, int second)
return first second;
有新的排序方式时,就再添加方法1
2
3
4
5
6
7
8
9
10
11
12public static bool AlphabeticalGreaterThan(int first, int second)
int comparison;
comparison(first.Tostring().CompareTor
second. Tostring());
return comparison >;
static void Main(string[] args)
int i;
int[] items = new int[5].
for (i-e; icitems.Length; it+)
Console.write("Enter an integer: ");
items[i] - int.Parse(Console, ReadLine());
Bubblesort(items, AlphabeticalGreaterThan); |这儿其实就是赋值了for (i = 8: i items. Length; i++) comparisonMethod =tconsole.WriteLine(items[41); AlphabeticalGreaterThan;
按字母排序与按数值排序的结果不同。可以看到,和本章开头描述的方式相比,现在添加一个附加的排序机制是多么简单!
要想按字母排序,唯一要做的就是添加A1phabeticalGreaterThan方法,然后在调用Bubblesort()的时候传递该方法。
通用的委托:System.Func和System.Action
为了减少自定义委托类型的必要,.NET3.5“运行时”库(对应C#3.0)包含了一组通用的委托,其中大多数都是泛型。System.Func系列委托代表有返回值的方法,而System.Action系列委托代表返回void的方法。下面展示了这些委托的签名。
Func和Action委托声明
1
2
3
4
5
6
7
8
9
10
11 public delegate void Action();
public delegate void Action<in T>(T arg);
public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2);
...
// 一直到16个参数
public delegate TResult Func<out TResult>();
public delegate TResult Func<in T, out TResult>(T arg);
public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
...
// 一直到16个参数