1.什么是委托,为什么要使用委托
我正在埋头苦写程序,突然想喝水,但是又不想自己去掉杯水而打断自己的思路,于是 我就想让女朋友去给我倒水。她去给我倒水,首先我得让她知道我想让她干什么,通知她之后我可以继续写自己的程序,而倒水的工作就交给了她。这样的过程就相 当于一个委托。
在 程序过程中,当程序正在处理某个事件的时候,我需要另外的程序代码去辅助处理一些事情,于是委托另一个程序模块去处理,而委托就可以达到这种目的,我可以 利用委托通知另外的程序模块,该去调用哪个函数方法。委托其实就起到了这样一个作用,将函数签名传递到了另一个函数中。或许这样讲还是有些模糊,看看后面 的具体实例。
2.委托的定义
delegate int Add(int num1,int num2);
delegate void
ConvertNum(string result);
上面是定义两个委托的例子,其实很简单。声明一个委托使用delegate关键字,上面分别是定义的带返回值的委托和不带返回值的委托,
两个委托都有传递参数,当然也可以不传递参数。
其实委托也是一个类,委托派生为System.MulticastDelegate,而System.MulticastDelegate
又继承System.Delegate,如果你
知道这个也就明白委托其实是一个特殊的类。
委托的简单实用例子
1
public
delegate
string
TeaDelegate(
string
spText);
2
3
public
class
DelegateSource
4
{
5
public
void
TestDelegate()
6
{
7
Operator op
=
new
Operator();
8
TeaDelegate tea
=
new
TeaDelegate(op.GetTea);
9
Console.WriteLine(
"
去给我倒杯水
"
);
10
Console.WriteLine();
11
string
result
=
tea(
"
去给我倒杯水
"
);
12
Thread.Sleep(
5000
);
13
Console.WriteLine(result);
14
Console.WriteLine();
15
}
16
}
17
18
public
class
Operator
19
{
20
///
<summary>
21
///
确定是否还有水
22
///
</summary>
23
private
bool
flag
=
true
;
24
25
public
string
GetTea(
string
spText)
26
{
27
if
(spText
==
"
去给我倒杯水
"
)
28
{
29
if
(flag)
30
{
31
return
"
老公,茶来了
"
;
32
}
33
else
34
{
35
return
"
老公,没有水了
"
;
36
}
37
}
38
return
"
等待.......
"
;
39
}
40
}
输出结果
上面使用最普通的一种方式来定义了一个委托的使用,这个例子虽然很简单,但是能够很形象的描述委托的使用。
3. 委托的三种形式
(1).推断
推断委托例子
1
public
delegate
string
TeaDelegate(
string
spText);
2
3
public
class
DelegateSource
4
{
5
public
void
TestDelegate()
6
{
7
Operator op
=
new
Operator();
8
TeaDelegate tea
=
op.GetTea;
9
Console.WriteLine(
"
去给我倒杯水
"
);
10
Console.WriteLine();
11
string
result
=
tea(
"
去给我倒杯水
"
);
12
Thread.Sleep(
5000
);
13
Console.WriteLine(result);
14
Console.WriteLine();
15
}
16
}
17
18
public
class
Operator
19
{
20
///
<summary>
21
///
确定是否还有水
22
///
</summary>
23
private
bool
flag
=
true
;
24
25
public
string
GetTea(
string
spText)
26
{
27
if
(spText
==
"
去给我倒杯水
"
)
28
{
29
if
(flag)
30
{
31
return
"
老公,茶来了
"
;
32
}
33
else
34
{
35
return
"
老公,没有水了
"
;
36
}
37
}
38
return
"
等待.......
"
;
39
}
40
}
在委托定义的例子中我们看到委托的使用方法是在委托实例化的时候指定的[new DelegateName(FunctionName)],这里可能表述不是太但是代码应该看得白了。 而委托的推断,并没有new 委托这个步骤,而是直接将Function 指定给委托。
(2).匿名函数
匿名委托例子
1
public
delegate
string
TeaDelegate(
string
spText);
2
3
public
class
DelegateSource
4
{
5
public
void
TestDelegate()
6
{
7
Operator op
=
new
Operator();
8
bool
flag
=
true
;
9
TeaDelegate tea
=
delegate
(
string
spText)
10
{
11
if
(spText
==
"
去给我倒杯水
"
)
12
{
13
if
(flag)
14
{
15
return
"
老公,茶来了
"
;
16
}
17
else
18
{
19
return
"
老公,没有水了
"
;
20
}
21
}
22
return
"
等待.......
"
;
23
};
24
25
Console.WriteLine(
"
去给我倒杯水
"
);
26
Console.WriteLine();
27
string
result
=
tea(
"
去给我倒杯水
"
);
28
Thread.Sleep(
5000
);
29
Console.WriteLine(result);
30
Console.WriteLine();
31
}
32
}
至于匿名委托,给人的感觉更为直接了,都不用显示的指定方法名,因为根本没有方法,而是指定的匿名方法。匿名方法在.NET 中提高了
代码的可读性和优雅性。对于更多操作较少的方法
直接写为匿名函数,这样会大大提高代码的可读性。这里有两个值得注意的地方: 第一,不能使用
跳转语句跳转到该匿名方法外,第二
不能使用ref,out修饰的参数
(3).多播委托
多播委托例子
1
public
delegate
string
TeaDelegate(
string
spText);
2
3
public
class
DelegateSource
4
{
5
public
void
TestDelegate()
6
{
7
Operator op
=
new
Operator();
8
9
TeaDelegate tea1
=
op.GetTea;
10
TeaDelegate tea2
=
op.Speak;
11
TeaDelegate tea
=
tea1
+
tea2;
12
13
Console.WriteLine(
"
去给我倒杯水
"
);
14
Console.WriteLine();
15
string
result
=
tea(
"
去给我倒杯水
"
);
16
Thread.Sleep(
5000
);
17
Console.WriteLine(result);
18
Console.WriteLine();
19
}
20
}
21
22
public
class
Operator
23
{
24
///
<summary>
25
///
确定是否还有水
26
///
</summary>
27
private
bool
flag
=
true
;
28
29
public
string
GetTea(
string
spText)
30
{
31
if
(spText
==
"
去给我倒杯水
"
)
32
{
33
if
(flag)
34
{
35
return
"
老公,茶来了
"
;
36
}
37
else
38
{
39
return
"
老公,没有水了
"
;
40
}
41
}
42
return
"
等待.......
"
;
43
}
44
45
46
public
string
Speak(
string
spText)
47
{
48
Console.WriteLine(
"
\n去把我的设计图稿拿来
"
);
49
return
null
;
50
}
51
}
还是上面的那个实例,我不尽想让女朋友去给我掉杯水,还让她帮我将程序设计图稿拿过来。这个时候做的就不是一件事了,而是多件。
程序中也有很多这种情况,于是我们需要多播委
托,在一个委托上指定多个执行方法,这是在程序中可以行的。上面提到了,委托直接继承于
System.MulticastDelegate,
正是因为这个类可以实现多播委托。如果调用多播委托,就可以按顺序连续调用多个方法。为此,委托的签名就必须返回void;否则,就只能得 到委托调用的最后一个方法的结果。所以在上面的这段代码中是得不到结果的
4.事件
使用C#编程,无论是 WinForm,WebForm 给人很难忘得就是它的控件,而他们的控件库使用方式都是使用使用事件驱动模式,而事件驱动模式却少不了委托。话不多说,看代码能够更清好的理解事件和委托 之间的联系.
事件的使用
1
public
delegate
void
MyDelegate(
string
name);
2
3
public
class
EventSource
4
{
5
public
event
MyDelegate Event_Delegate;
6
7
public
void
SetCustomer(
string
name)
8
{
9
Console.WriteLine(
"
事件发 生.....\n
"
);
10
Console.WriteLine(
"
hi!
"
+
name);
11
}
12
13
public
void
TestEvent()
14
{
15
EventSource source
=
new
EventSource();
16
Console.WriteLine(
"
订阅事件.....\n
"
);
17
source.Event_Delegate
+=
new
MyDelegate(source.SetCustomer);
18
Console.WriteLine(
"
触发事 件.....\n
"
);
19
source.Event_Delegate(
"
hechen
"
);
20
Console.WriteLine(
"
..................
"
);
21
}
22
}
上面的代码中我们定义了一个委托,然后定义了一个类EventSource,这个类中声明了一个事件。定义一个事件使用event 关键字,定义一
个event必须指定这个event传递消息的委托,在触发事件之前必需订阅事件,我们使用+=
new 语法来订阅一个事件,也就相当于实例化一个事件。
当我们触发事件的时候,就会调用相应的方法去处理。
5. 泛型委托
委托是类型安全的引用,泛型委托就和我们常用的泛型类一样,这个类在使用的时候才能确定类型.通过泛型委托,我们可以在委托传递参数
之后知道它的类型.在.NET中有一个很典型的
泛型委托:
public delegate voie EventHandler<TEventArgs>(object
sender,TEventArgs e) where TEventArgs:EventArgs.
这是一个非常有特色的泛型委托,可能我们用的比较
少,但是作用是不能忽视的。
我们看看三个非常具有代表性的泛型委托.现在.NET4.0已经出来了,但是泛型委托.NET2.0就出来了,Linq 大家用的那叫一个甜,
为啥
函数式编程风格,匿名方法,Lamda表达式表达式使用是如此的魅力。但是大家仔细观察过没有,Linq 中的方法有几个经常出现的参数:
Action<T>,Predicate<T>,Func<T,
Result>
Func<T, E>:封装一个具有一个参数并返回 E 参数指定的类型值的方法,T 是这个委托封装方法的参数类型,E是方法的返回值类型。当然Func<T, Result> 只是其中的一种情况,这个委托还有其他的几种情况:Func<T> 这个是方法没有参数,返回值类型是T;Func<T1,T2,Result> 这个方法有两个参数,类型分别为T1,T2,返回值是Result,还有 Func<T1,T2,T3,Result>,Func<T1,T2,T3,T4,Result> 这几中情况,具体情况就不介绍了.我们还可以通过扩展类型,扩展为更多的参数.
Func
委托的使用
1
public
void
TestFunc()
2
{
3
TEventSource eventSource
=
new
TEventSource();
4
Func
<
string
,
string
>
func
=
eventSource.GetTea;
5
string
result
=
func(
"
茶
"
);
6
Console.WriteLine(result);
7
}
8
9
public
string
GetTea(
string
context)
10
{
11
if
(context
==
"
茶
"
)
12
{
13
return
"
茶来了
"
;
14
}
15
else
16
{
17
return
"
设计稿子来了
"
;
18
}
19
}