一.在C#中我们可以使用delegate关键字来声明一个委托,声明的格式如下:
delegate <函数返回类型> <委托名> (<函数参数>)
下面我们来看一下代码是如何来写的:
[C]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
namespace
DelegateAndLambda
{
delegate
int
MyDelegate(
int
x,
int
y);
class
MainClass
{
public
static
int
Sum(
int
a,
int
b)
{
return
a + b;
}
public
static
void
Main(string[] args)
{
MyDelegate mydel = Sum;
Console.WriteLine(mydel(2, 3));
}
}
}
|
在名字空间内部,类的外部创建一个委托类型MyDelegate,其声明的格式为返回值int类型,参数有两个,都是int类型.大家可以看到该委托的格式与下面的Sum的方法格式外形上来看是一致的,在Main方法里面,我们就可以把Sum方法直接赋值给MyDelegate的一个变量,此时我们可以说把一个方法绑定给了一个委托变量,在打印语句里面,直接调用了委托变量,输出的结果是5 ,和直接用Sum这个方法得到的结果是一样的.
从上面的代码我们可以得到如下的结论:
1.在声明委托时有一定的格式,我们就可以把与该委托格式一样的方法名直接赋值给该委托的一个变量;
2.从某种意义上来讲,委托相当于一个方法模版,把与该模版相同的方法赋值给委托变量后,使用委托变量时,本质上来讲就是使用其所绑定的方法;
3.委托变量的使用与方法的使用是一样的,如果有参数,委托变量后面带小括号,里面要加参数,并且个数和类型要一致,如果没有参数,也要加小括号;
(其它三种格式的委托大家可以自己练习试着做一下)
委托不仅可以绑定一个方法,还可以绑定多个方法,如下代码:
[C]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
namespace
DelegateAndLambda
{
delegate
void
Greet(string str);
class
MainClass
{
public
static
void
ChineseGreet(string str)
{
Console.WriteLine(
"早上好!!"
+ str);
}
public
static
void
EnglishGreet(string str)
{
Console.WriteLine(
"Good Morning!!"
+ str);
}
public
static
void
Main(string[] args)
{
Greet greet = ChineseGreet;
greet += EnglishGreet;
greet(
"小李"
);
}
}
}
|
我们在给委托变量绑定第一个方法时,直接用赋值运算符,这样才可以使委托变量进行实例化,否则是不能用的,绑定第二个方法时,用+=运算符,这样委托变量就同时绑定了两个方法,在Main方法里面使用委托时,会调用所绑定的两个方法,把结果打印出来,所绑定的方法是有先后顺序的,先绑定的方法会先执行.使用委托时只需要记住一点:即委托所绑定的方法从格式上二者要一致.委托不仅可以绑定静态方法,也可以绑定实例方法,这个大家可以自己试一下.
以上的两个例子,委托在绑定方法时,都会先把方法创建好,下面讲另外两种委托,匿名委托和lambda表达,这样就不用再创建方法了.下面的代码是把上面的例子用匿名委托改写的,代码如下:
[C]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
|
public
static
void
Main(string[] args)
{
Greet greet = delegate(string str)
{
Console.WriteLine(
"早上好!!"
+ str);
};
greet += delegate(string str)
{
Console.WriteLine(
"Good Morning!!"
+ str);
};
greet(
"小李"
);
}
|
其结果与上面的一样,使用匿名委托时其格式为:delegate(){};小括号里面为参数,大括号里面是方法体,具体是否有返回值,以及参数个数和参数类型要与其所赋值的委托格式一致,如果有返回值,则只需要在大括号里面return一个返回值即可;如把上面的第一个例子用匿名委托更改如下:
[C]
纯文本查看
复制代码
1
2
3
4
|
MyDelegate mydele = delegate(
int
a,
int
b)
{
return
a + b;
};
|
由于MyDelegate的格式为:返回值为int类型,参数2个,都是int类型,所以匿名委托的参数也要有两个并且都是int类型,在{}里面要有一个返回值.下面再看另外一种匿名委托,lambda表达式,其在C++中称为闭包,其格式如下: (()=>{}),具体的使用方法请看以下代码:
[C]
纯文本查看
复制代码
1
2
3
4
5
|
MyDelegate mydele = ((
int
a,
int
b) =>
{
return
a + b;
});
Console.WriteLine(mydele(2, 3));
|
结果和上面的例子是一样的.大家只需要记住其格式即可,(()=>{}),小括号里时面是参数,大括号里面是方法体.只要大家看得多了,就会习惯这种编程方法了.
二.上面的例子我们看到,定义的委托都是在类外面,名字空间下面的,所有的类都可以用该委托,这与我们讲的面向对象的三大特性封装是相违背的,但是当我们把一个委托放到类里面时,不管该委托前面有没有用public访问修饰符修饰,在类的外面都是取不到的.此时就需要使用event关键字,首先在类里面先声明一个委托,然后在类里面定义一个委托的变量,前面用event来修饰,这样在类的外面就可以直接拿到委托变量了,然后就能够绑定方法了,但绑定方法时,全部要进行+=操作,这一点与上面的例子有点不同,具体的使用方法如下:
[C]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
class
MainClass
{
public
delegate
void
Greet(string str);
public
event Greet greet;
public
static
void
ChineseGreet(string str)
{
Console.WriteLine(
"早上好!!"
+ str);
}
public
static
void
EnglishGreet(string str)
{
Console.WriteLine(
"Good Morning!!"
+ str);
}
public
static
void
Main(string[] args)
{
MainClass mc =
new
MainClass();
mc.greet += ChineseGreet;
mc.greet += EnglishGreet;
mc.greet(
"小李"
);
}
}
|
输出的结果和上面的例子一样.
三.把一个方法赋值给一个委托变量的这种机制,可以实现把方法当做参数进行传递,如果一个方法,其参数是一个委托,则调用该方法时,就可以把与该委托格式一样的方法传给形参.下面的代码以List集合类型里面的RemoveAll方法为例:
[C]
纯文本查看
复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
List<
int
> list =
new
List<
int
>();
list.Add(123);
list.Add(123);
list.Add(234);
list.Add(123);
list.RemoveAll((
int
a) =>
{
if
(a == 123)
{
return
true
;
}
return
false
;
});
foreach (
int
s in list)
{
Console.WriteLine(s);
}
|
先看一下RemoveAll方法的原型,public int RemoveAll(Predicate<T> match);进入到API里面,看到其形参是一个委托类型,原型为:public delegate bool Predicate<in T>(T obj);T为泛型类型占位符,上面的代码中list中用的是int类型,故此委托里面的T也为int,委托的原型为返回值为bool类型,参数为int类型,在调用方法时,用的是lambda表达式,参数是int类型的a,方法体里面的返回值类型为bool类型,a会遍历集合里面的所有元素,只要等于123,返回为true,就会把123从集合里面删除掉,遍历完一遍后,把集合里面的123全部删除了,输出的结果只有234了. |