一 什么是委托
可以认为委托是持有一个或多个方法的对象。可以执行委托,执行时委托会执行它所持有的方法。
从C++的角度理解,委托可以看成一个类型安全、面向对象的C++函数指针。
delegate void MyDel(int value); //声明委托类型
class Program
{
void PrintLow(int value)
{
Console.WriteLine($"Low value is {value}");
}
void PrintHigh(int value)
{
Console.WriteLine($"High value is {value}");
}
static void Main(string[] args)
{
var program = new Program();
MyDel myDel; //声明一个Mydel委托类型的变量myDel;
var random = new Random().Next(99);
if (random < 50)
{
myDel = new MyDel(program.PrintLow); //创建一个包含PrintLow的委托对象将其赋值给myDel变量;
}
else
{
myDel = new MyDel(program.PrintHigh);
}
myDel(random); //执行委托
}
}
二 委托概述
委托和类一样,是一种用户自定义的类型。
可以把委托看作一个包含有序方法列表的对象,这些方法具有相同的签名和返回类型。
- 方法的列表称为调用列表;
- 委托持有的方法可以来自任何类或结构,只有方法的返回类型和签名是匹配的;
- 调用列表中的方法可以是实例方法也可以是静态方法;
- 在调用委托时,会执行其列表中的所有方法。
三 声明委托类型
delegate void MyDel(int value); //声明委托类型
声明委托类型以delegate关键字开头,指定了MyDel类型的委托只会接受无返回值并且有单个int参数的方法。
四 创建委托对象
委托是引用类型,因此有引用和对象。在委托类型声明之后,我们可以声明变量并创建委托类型的对象。
有两种创建委托对象的方式。
delegate void MyDel(int value); //声明委托类型
class MyInstClass
{
public void MyM1(int value)
{
Console.WriteLine($"MyM1:{value}");
}
}
class SClass
{
public static void OtherM2(int value)
{
Console.WriteLine($"OthreM2{value}");
}
}
class Program
{
static void Main(string[] args)
{
var myInstObj = new MyInstClass();
// 1. 带new运算符的对象创建
var delVar = new MyDel(myInstObj.MyM1);
var dVar = new MyDel(SClass.OtherM2);
// 2. 快捷语法
MyDel delVar1 = myInstObj.MyM1;
MyDel dVar1 = SClass.OtherM2;
// 给委托变量赋值会改变包含在委托变量中的引用
delVar = SClass.OtherM2;
}
}
五 组合委托
以下代码创建了两个委托,第三个委托有前两个委托组合而成。
MyDel delA = myInstObj.MyM1;
MyDel delB = SClass.OtherM2;
MyDel delC = delA + delB; //组合调用列表
六 为委托添加方法
委托其实是不变的,不过C#提供了看上去可以为委托添加方法的语法,使用+=运算符;
var inst = new MyInstClass();
MyDel delVar = inst.MyM1; //创建委托
delVar += SCL.M3; //增加方法
delVar += X.ACT; //增加方法
在使用+=运算符时,实际发生的是创建了一个新的委托,其调用列表是左边的委托加上右边方法的组合,然后将这个新的委托赋值给delVar。
七 为委托移除方法
使用 -=运算符为委托移除方法。
delVar -= SCL.M3; //从委托移除方法
与委托增加方法一样,其实是创建了一个新的委托;
如果调用列表中的方法有多个实例,-=运算符将从列表最后开始搜索,并且移除第一个与方法匹配的实例;
试图删除委托中不存在方法没有效果;
试图调用空委托会抛出异常。
八 调用委托
可以像调用方法一样简单地调用委托。用于调用委托的参数将会用于调用列表中的每一个方法。
var inst = new MyInstClass();
MyDel delVar = inst.MyM1; //创建委托
delVar += SCL.M3; //增加方法
delVar += X.ACT; //增加方法
delVar(55); //调用委托
九 调用带返回值的委托
如果委托有返回值并且其调用列表中有一个以上的方法:
- 调用列表中最后一个方法的返回值就是委托调用返回的值;
- 调用列表中其他方法的返回值会被忽略。
十 调用带引用参数的委托
如果委托有引用参数,参数值会根据调用列表中的一个或多个方法的返回值而改变。
delegate void MyDelegate(ref int x);
class Program
{
public static void Add2(ref int x) { x += 2; }
public static void Add3(ref int x) { x += 3; }
static void Main(string[] args)
{
MyDelegate myDel = Program.Add2;
myDel += Program.Add3;
int x = 5;
myDel(ref x);
Console.WriteLine(x);
Console.Read();
}
}
输出:10
十一 匿名方法
匿名方法是在初始化委托时内联声明的方法。
delegate int MyDelegate(int x);
class Program
{
static void Main(string[] args)
{
//使用匿名方法
MyDelegate myDel = delegate (int x)
{
return x + 20;
};
//委托的返回类型是int,则匿名方法中的代码也必须返回int类型的值。
}
}
可以在如下地方使用匿名方法:
- 声明委托变量时作为初始化表达式;
- 组合委托时在赋值语句的右边;
- 为委托增加事件时在赋值语句的右边。
匿名方法表达式的语法包含:
- delegate关键字;
- 参数列表,如果没有参数则可以省略;
- 语句块,包含了匿名方法的代码。
匿名方法不会显示声明返回值,而其中的代码行为必须通过返回一个在类型上与委托的返回类型相同的值来匹配委托的返回类型。
如果委托有void类型的返回值,匿名方法就不能返回值。
十二 Lambda表达式
C#3.0引入了Lambda表达式,简化了匿名方法的语法,直接描述方法的定义。
delegate int MyDelegate(int x);
class Program
{
static void Main(string[] args)
{
MyDelegate myDel = delegate (int x) { return x + 1; }; //匿名方法
MyDelegate myDel1 = (int x) => { return x = 1; }; //Lanbda表达式
MyDelegate myDel2 = (x) => { return x = 1; }; //Lanbda表达式
MyDelegate myDel3 = x => { return x = 1; }; //Lanbda表达式
MyDelegate myDel4 = x => x = 1; ; //Lanbda表达式
}
}