您的位置:首页 > 编程语言 > C#

第七章:委托和事件--委托(c#高级编程 第6版)

2011-04-24 21:52 351 查看

委托概述:

.net以委托的方式实现了函数指针的概念,与C语言不同,.net的委托是类型安全的。

当把方法传送给其他方法时,需要使用委托。

关于委托的类型安全:委托封装了方法的细节,定义委托时必须给出它所代表的方法签名和返回类型等全部细节。

1. 声明委托

定义委托,就是告诉编译器这种类型的委托代表了那种类型的方法,然后创建该委托的一个或多个实例。编译器在后台将创建表示该委托的一个类。

理解委托的一种好方式是把委托当做给方法签名和返回类型指定名称,定义委托时必须给出它所代表的方法签名和返回类型等全部细节。

定义一个委托,基本上是定义一个新类,所以可以在定义类的任何地方定义委托,既可以在另一个类的内部定义,也可以在任何类的外部定义。

委托的声明例子:

delegate double DoubleOp(double x);


表示该委托代表一个接受double参数,返回一个double值的方法。

2. 使用委托

委托在语法上总是带有一个参数的构造函数,这个参数就是委托引用的方法:这个方法必须匹配最初定义委托时的签名。

调用委托时,必须提供委托实例的名称,后面的括号中应包含调用委托中的方法时使用的参数。

给委托实例提供括号与使用该实例的Invoke方法完全相同。

委托推断:为减少输入,也可以直接将方法的地址赋给委托实例,编译器可以把委托实例解析为特定的类型。

调用时只需要被调用的方法签名是正确的,并不考虑调用此方法的是什么类型,也不管这个方法静态与否。

委托的使用例子:

class Program
{
//委托的定义
private delegate string GetAString();

static void Main()
{
int x = 40;
// 委托的实例化
GetAString firstStringMethod = new GetAString(x.ToString);
//或者使用如下委托推断实例化
// GetAString firstStringMethod = x.ToString;
// 委托的调用
Console.WriteLine("String is " + firstStringMethod());
// 或者使用Invoke方法调用
Console.WriteLine("String is " + firstStringMethod.Invoke());
// 以上的委托调用等同于下面一句:
Console.WriteLine("String is" + x.ToString());
}


3. 使用委托的两个例子:

委托数组

using System;
using System.Collections.Generic;
using System.Text;

namespace Wrox.ProCSharp.Delegates
{
class MathOperations
{
public static double MultiplyByTwo(double value)
{
return value * 2;
}

public static double Square(double value)
{
return value * value;
}
}

delegate double DoubleOp(double x);

class MainEntryPoint
{
static void Main()
{
// 定义委托数组
DoubleOp[] operations =
{
MathOperations.MultiplyByTwo,
MathOperations.Square
//new DoubleOp(MathOperations.MultiplyByTwo),
//new DoubleOp(MathOperations.Square)
};

for (int i = 0; i < operations.Length; i++)
{
Console.WriteLine("Using operations[{0}]:", i);

// 将方法(委托)传给方法
ProcessAndDisplayNumber(operations[i], 2.0);
ProcessAndDisplayNumber(operations[i], 7.94);
ProcessAndDisplayNumber(operations[i], 1.414);
Console.WriteLine();
}
}

static void ProcessAndDisplayNumber(DoubleOp action, double value)
{
// 调用委托
double result = action(value);
Console.WriteLine(
"Value is {0}, result of operation is {1}", value, result);
}
}
}


为什么要用委托?另一个例子
例如,如何写一个能给任何对象排序的冒泡排序函数Sort()?由于不知道如何具体比较两个对象的大小,这样实现起来很困难。
一个解决办法是:给某一类型的对象数组排序时,将比较这一类型两个对象大小的方法也传给Sort函数,通过委托来实现。
1. 排序函数的实现:

using System;
using System.Collections.Generic;
using System.Text;

namespace Wrox.ProCSharp.Delegates
{
// 定义委托
delegate bool Comparison(object x, object y);

class BubbleSorter
{
// 接受特定类型的比较方法做为参数
static public void Sort(object[] sortArray, Comparison comparer)
{
for (int i = 0; i < sortArray.Length; i++)
{
for (int j = i + 1; j < sortArray.Length; j++)
{
// 使用特定类型的方法进行比较
if (comparer(sortArray[j], sortArray[i]))
{
object temp = sortArray[i];
sortArray[i] = sortArray[j];
sortArray[j] = temp;
}
}
}
}
}
}


2. 要比较的类型定义

匿名方法例子

using System;

namespace Wrox.ProCSharp.Delegates
{
class Program
{
delegate string DelegateTest(string val);

static void Main()
{
string mid = ", middle part,";

// 使用匿名方法,减少代码量
DelegateTest anonDel = delegate(string param)
{
param += mid;
param += " and this was added to the string.";
return param;
};

Console.WriteLine(anonDel("Start of string"));
}
}
}


使用匿名方法注意事项:
必须遵循两个原则
(1)在匿名方法中不能使用跳转语句跳到该匿名方法的外部
(2)反之亦然,外部跳转语句不能跳转到匿名方法的内部

匿名方法内部不能访问不安全的代码

匿名方法不能访问在该方法外部定义的ref和out参数,但可以使用在匿名方法外部定义的其他变量

如果需要匿名方法多次编写同一个功能,就不要使用匿名方法
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: