最近一直在实习,觉得自己的基础还是不行,遇到复杂的代码还是很不容易理解,于是又重温了《C#本质论》,便写下了这篇笔记。
委托(delegate):允许像处理其他任何数据一样处理对方法的引用。
Lambda表达式:可以快速和简单地创建自定义委托。
委托
C/C++中允许程序员利用”函数指针“将对方法的引用作为实参传递给另一个方法。C#则使用委托提供了相同的功能。委托允许捕捉对方法的引用,并像传递其他对象那样传递这个引用,像调用其他方法那样调用这个被捕捉的方法。
来看下边这个冒泡排序
class SimpleSort1
{
public static void BubbleSort(int[] items)
{
int i, j, temp;
if (items == null)
{
return;
}
for (i = items.Length - 1; i >= 0; i--)
{
for (j = 1; j <= i; j++)
{
if (items[j - 1] > items[j])
{
temp = items[j - 1];
items[j - 1] = items[j];
items[j] = temp;
}
}
}
}
}
该方法实现了对整数数组的升序排列,但是如果我们现在想要升级或者降序来排列整数数组,就会有两个方案可以选择:1.复制上述代码,然后将 >操作符 改为 <操作符 。但是复制这么多代码知识改变了一个操作符,似乎不是很合理。2.传递一个附加参数,指出如何排序,看下边的代码。
class SimpleSort2
{
public enum SortType
{
Ascending, //升序
Descending //降序
}
public static void BubbleSort(int[] items, SortType sortOrder)
{
int i, j, temp;
if (items == null)
{
return;
}
for (i = items.Length-1; i >= 0; i--)
{
for (j = 1; j <= i; j++)
{
bool swap = false;
switch (sortOrder)
{
case SortType.Ascending:
swap = items[j - 1] > items[j];
break;
case SortType.Descending:
swap = items[j - 1] < items[j];
break;
}
if (swap)
{
temp = items[j - 1];
items[j - 1] = items[j];
items[j] = temp;
}
}
}
}
}
通过传递第二个参数来判断是升序还是降序,但是上述代码也只是照顾了两种可能的排序方式,如果想要按照其他更多的方式进行排序,那么SortType值以及switch分支的数量将会变得很恐怖。
这个时候,为了增强灵活性和减少代码重复,可以将比较方法直接作为参数传递给BubbleSort()方法。为了能将方法作为参数传递,必须要有一个能够表示方法的数据类型。这就是大名鼎鼎的委托,因为它委托调用对象所引用的方法,来看下边的代码。本例中委托的数据类型是ComparisonHandler。
//声明一个委托类型
public delegate bool ComparisonHandler(int first, int second);
class DelegateSample
{
public static void BubbleSort(int[] items, ComparisonHandler comparisonMethod)
{
int i, j, temp;
if (comparisonMethod == null)
{
throw new ArgumentNullException("comparisonMethod");
}
if (items == null)
{
return;
}
for (i = items.Length - 1; i >= 0; i--)
{
for (j = 1; j <= i; j++)
{
if (comparisonMethod(items[j - 1], items[j]))
{
temp = items[j - 1];
items[j - 1] = items[j];
items[j] = temp;
}
}
}
}
//声明一个与委托类型兼容的方法(参数和返回值必须兼容委托的签名)
public static bool GreaterThan(int first, int second)
{
return first > second;
}
static void Main()
{
int i;
int[] items = new int[5];
for (i = 0; i < items.Length; i++)
{
Console.WriteLine("Enter an integer: " );
items[i] = int.Parse(Console.ReadLine());
}
BubbleSort(items, GreaterThan);
for (i = 0; i < items.Length; i++)
{
Console.WriteLine(items[i]);
}
}
}
你也可以通过改变BubbleSort()的第二个参数(声明另一个与委托类型兼容的方法),来改变排序方式。与文章开头的方法相比,现在的方法要简单许多。
如果你将委托声明在DelegateSample或者另一个类的内部,那么委托类型就会成为嵌套类型。
注意,ComparisonHandler委托是引用类型,但是你不必用new关键字去实例化它。从C#2.0开始,从方法组(为方法命名的表达式)向委托类型的转换会自动创建一个新的委托对象。
Lambda表达式
在上边的代码中。我们注意到了GreaterThan方法的声明(public static bool GreaterThan(int first, int second))比主体( return first > second;)复杂多了,这么简单的方法还需要这么复杂的准备,目的竟然只是为了转换为委托类型,显然不合适。
为了解决这个问题,C#2.0引入了非常精简的语法创建委托(匿名函数),C#3.0则更加精简(Lambda表达式),两种语法统称为匿名函数(anonymous function)。我们应该优先使用Lambda表达式。
Lambda表达式的目的在于需要基于很简单的方法生成委托时,避免声明全新成员的麻烦,来看下边的代码
BubbleSort(items, (int first, int second) => {return first < second;});
上述代码可以将BubbleSort()的第2个参数理解为”整数first和second用于返回first<second的结果“。和之前的调用相比,围绕方法的准备工作大大减少了。然而,语法还是有些长,从委托类型就可以推断出Lambda表达式必然返回bool,同样可以推断出两个参数必然是int类型,看下边的代码
BubbleSort(items, (first, second) => {return first < second;});
通常,只要编译器能从Lambda表达式所转换成的委托推断出类型,所有Lambda表达式都不需要显式声明参数类型。然而,若指定类型能使代码更易读,C#也允许这样做。在不能推断出类型的情况下,C#要求显式地指定Lambda參数类型。只要显式指定了一个Lambda参数类型,所有参数类型都必须被显式指定,而且必须和委托参数类型完全一致。
当然你也可以使用更简单的表达式Lambda来传递委托
BubbleSort(items, (first, second) => first < second);
在阅读上述代码时,可以理解为“first和second满足frst小于second的条件”。
下表总结了Lambda表达式的注意事项: