本文主要介绍委托、事件和Lambda表达式。
1. 委托
1.1 委托使用
委托是一种类型,表示对具有特定参数列表和返回类型的方法的引用,可以理解为方法指针。 当实例化委托时,我们可以将委托实例与具有相同参数列表和返回类型的任何方法相关联。 我们可以通过委托实例调用方法。
委托的声明如下:
public delegate 方法返回值 delegateName([参数列表]);
案例如下:
class Program
{
// 定义委托,没有返回值,没有参数
public delegate void MyDelegate();
// 定义方法,没有返回值,没有参数
public static void greet()
{
Console.WriteLine("hello world!");
}
static void Main(string[] args)
{
// 实例化委托
MyDelegate myDelegate = new MyDelegate(Program.greet);
// 通过委托调用方法
myDelegate();
}
}
可以将任何来自类或结构的与委托类型匹配的任何方法分配给委托,分配给委托的方法既可以是静态方法,也可以是实例方法。
我们可以使用命名方法来实例化委托:
MyDelegate myDelegate = new MyDelegate(Program.greet); // 方法一,C# 1.0
MyDelegate myDelegate = Program.greet; // 方法二,C# 2.0
委托使得方法可以作为方法参数,传递到另一个方法中去。
class Program
{
// 定义委托
public delegate void MyDelegate();
// 定义与委托相匹配的方法
public static void greet()
{
Console.WriteLine("hello world!");
}
// 定义参数为委托的方法
public static void methodWithDelegateParameter(MyDelegate myDelegate)
{
myDelegate();
}
static void Main(string[] args)
{
// 直接将方法作为参数传递给另一个方法
methodWithDelegateParameter(Program.greet);
}
}
1.2 委托链
什么是委托链?在之前的简单例子中,委托实例只包裹了一个方法,委托链就是一个委托实例包裹多个匹配的方法。我们可以使用 +
运算符来为委托实例添加匹配的方法,也可以使用 -
运算符来为委托实例减少匹配的方法。
class Program
{
// 定义委托
public delegate void MyDelegate();
// 定义与委托匹配的方法
public static void greetEnglish()
{
Console.WriteLine("hello world!");
}
public static void greetChinese()
{
Console.WriteLine("你好");
}
static void Main(string[] args)
{
// 委托链,添加方法
MyDelegate myDelegate = Program.greetEnglish;
myDelegate += Program.greetEnglish;
myDelegate += Program.greetChinese;
myDelegate += Program.greetEnglish;
// 委托链,减少方法
myDelegate -= Program.greetEnglish;
// 通过委托调用方法
myDelegate();
}
}
结果:
hello world!
hello world!
你好
通过上述例子,我们可以总结如下:
- 通过委托调用方法时,会依次调用委托链上的所有方法;
- 从委托链中除去方法时,如果有多个同名方法,则除去最后加进去的方法;
2. 事件
什么是事件?小到单击鼠标右键是事件,按下空格键是事件,大到下雨是事件,奥运会开幕也是事件。
在事件中,存在两个角色:事件发布者和事件订阅者。事件发布者,就是发布消息的角色,事件订阅者,就是收到事件发生消息或者处理事件的角色。
事件与我们的程序有什么关系呢?当然有关。例如,当我们按下视频播放按钮时,视频播放。在这件事中,按钮充当了事件发布者,他告诉事件订阅者事件发生了,然后事件订阅者接收到消息,做出播放视频的动作。
在C# 中,定义事件的语法如下:
访问修饰符 event 委托类型 事件名;
- 访问修饰符一般定义为public,因为事件的订阅者需要对事件进行订阅和取消订阅的操作,定义为public可使事件对其它类可见;
- 委托类型,可以理解为事件订阅者,事件发生时,就可以通过委托,调用一系列方法做出处理;
如何订阅和取消订阅事件?
既然在事件定义中,我们使用了委托,并且委托就是事件订阅者,那么就可以使用+
和-
来订阅和取消订阅事件。
首先来看一个简单的事件程序,自定义一个键盘类,一个按键信息类,当按下键盘这一事件发生时,按键信息类会收到信息,并显示按下的按钮。
class Event
{
/// <summary>
/// 主类
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
Keyboard keyboard = new Keyboard();
// 添加事件订阅者
keyboard.keyboadPressedEvent += new KeyboardMonitor("monitor 1").receiveMsg;
keyboard.keyboadPressedEvent += new KeyboardMonitor("monitor 2").receiveMsg;
keyboard.keyboadPressedEvent += new KeyboardMonitor("monitor 3").receiveMsg;
keyboard.receive();
}
}
/// <summary>
/// 键盘类
/// </summary>
class Keyboard
{
public delegate void KeyboardPressedHandler(char key);
public event KeyboardPressedHandler keyboadPressedEvent;
// 当按键事件发生时,通知事件订阅者
public void onKeyboardPressed(char key)
{
if (keyboadPressedEvent != null)
{
keyboadPressedEvent(key);
}
}
// 接受键盘按键
public void receive()
{
while (true)
{
char key = Console.ReadKey().KeyChar;
Console.WriteLine();
onKeyboardPressed(key);
}
}
}
/// <summary>
/// 键盘检测器
/// </summary>
class KeyboardMonitor
{
String name;
public KeyboardMonitor(String name)
{
this.name = name;
}
// 当监控到按键事件发生时,输出提示信息
public void receiveMsg(char key)
{
Console.WriteLine("{0}检测到{1}按下了...", this.name, key);
}
}
运行结果如下:
3. Lambda表达式
3.1 委托进化史
在C# 1.0 ,我们需要通过new和命名方法来实例化委托:
MyDelegate mydalagate = new MyDelegate(namedMethod);
在C# 2.0,我们可以直接使用命名方法来实例化委托了:
MyDelegate mydalagate = namedMethod;
同样,我们也可以使用匿名方法来实例化委托:
// Instantiate Del by using an anonymous method.Delegate
mydelegate = delegate(string name)
{
Console.WriteLine($"Notification received for: {name}");
};
语法格式为:
Delegate mydelegate = delegate(参数列表){匿名方法体};
在C# 3.0,我们可以使用Lambda表达式来实例化委托:
// Instantiate Del by using a lambda expression.Delegate
mydelegate = name =>
{
Console.WriteLine($"Notification received for: {name}");
};
语法格式为:
Delegate mydelegate = (参数列表) => {方法体};
3.2 Lambda方法体
Lambda表达式由 =>
分为两个部分,左边是方法参数列表,右边是方法体。
Lambda有两种形式:
(input parameters) => expression
(input parameters) => { <sequence-of-statements> }
对于方法参数列表,有以下规则:
- 如果没有方法参数,则圆括号不能被省略;
- 如果只有一个方法参数,则圆括号可以被省略;
- 如果有多个方法参数,则圆括号不能被省略,并且参数之间以逗号隔离;
- 我们可以省略方法参数的数据类型;
4. 参考资料
[1] Delegates - C# Programming Guide | Microsoft Docs
[2] Events - C# Programming Guide | Microsoft Docs
[3] Anonymous functions - C# Programming Guide | Microsoft Docs