一 什么是委托

可以认为委托是持有一个或多个方法的对象。可以执行委托,执行时委托会执行它所持有的方法。

从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表达式
}
}

最新文章

  1. iOS之加密的三种方法
  2. ImportError: No module named setuptools 解决方案
  3. [VBS]遍历XML文档
  4. CENTOS 基础指令——查看系统环境
  5. 微信开发学习日记(八):7步看懂weiphp插件机制,核心目标是响应微信请求
  6. dump json 显示中文问题
  7. 采用Bash脚本性能监控过程
  8. 谈话Java在ThreadLocal理解类
  9. Zookeeper:分布式程序的基石
  10. CSS实现父元素半透明,子元素不透明
  11. 【转】Nginx的启动、停止与重启
  12. Appium 测试微信小程序 Webview
  13. python学习(一)--python解释器
  14. Java EntityMapper
  15. PyQt5---ChangeIcon
  16. Centos7.4下keepalived-1.3.5的安装使用
  17. C++中的类成员指针
  18. Day7 Tomcat和servlet
  19. Android——android weight 属性(百度)
  20. 达人篇:2.1)APQP产品质量先期策划

热门文章

  1. 串口应用:遵循uart协议发送N位数据(状态优化为3个,适用任意长度的输入数据,取寄存器中的一段(用变量作为边界))
  2. 常用的函数式接口Consumer接口练习字符串拼接输出
  3. 2501-Logback的使用与配置范例xml
  4. 并发编程原理学习-reentrantlock源码分析
  5. playwright录制脚本
  6. 抖音 滑块验证方案 s_v_web_id 参数分析
  7. Camera类定义和实现
  8. WPF主窗体调用 User32的SetWindowPos 设置窗体置顶会导致与其他窗体抢夺焦点的问题
  9. 什么是 DevOps?看这一篇就够了!
  10. luogu1419 寻找段落 (二分,单调队列)