C#之委托🔥
# 什么是委托
在 C# 中,委托(Delegate)是一种类型,它可以存储对方法的引用,并且可以在运行时动态地将这些引用传递给其他方法。简而言之,委托可以看作是方法的类型安全的指针。
委托允许您将方法作为参数传递给其他方法,或者在运行时确定要调用的方法。这使得委托在事件处理、回调函数等方面非常有用。委托的声明类似于方法的声明,但它具有 delegate 关键字。委托可以被实例化,并且可以将方法添加到委托的调用列表中,这样当委托被调用时,它所包含的所有方法都将被依次调用。
# 1、委托声明
委托可以声明在类外部,也可以在类内部
// 定义一个委托,它可以指向一个接受一个字符串参数并返回 void 的方法
public delegate void MyDelegate(string message);
// 一个方法,符合委托的签名
public void DisplayMessage(string message)
{
Console.WriteLine("Message: " + message);
}
2
3
4
5
6
7
# 2、委托实例化
a、通过new实例化
MyDelegate myDelegate = new MyDelegate(DisplayMessage);
b、将方法分配给委托类型
MyDelegate myDelegate = DisplayMessage;
c、声明匿名方法
MyDelegate myDelegate = Delegate(string message){Console.WriteLine("Message: " + message);}
d、使用 lambda 表达式
MyDelegate myDelegate = message => Console.WriteLine("Message: " + message);
# 3、委托调用
myDelegate.invoke("委托被调用了!");
//或者
myDelegate("委托被调用了!");
2
3
# 委托实战
首先假设我们有一个场景,根据用户输入的不同命令执行不同的操作。使用委托可以让我们以一种更加模块化和可维护的方式组织代码,而不是将所有的逻辑都塞进一个庞大的 switch 语句中。 让我们首先使用 switch 语句来实现功能:
class Program
{
static void Main(string[] args)
{
// 模拟用户输入的命令
string userInput = "sayHello";
// 根据用户输入的命令执行相应的操作
switch (userInput)
{
case "sayHello":
SayHello();
break;
case "sayGoodbye":
SayGoodbye();
break;
default:
Console.WriteLine("Invalid command.");
break;
}
}
// 不同命令对应的方法
static void SayHello()
{
Console.WriteLine("Hello!");
}
static void SayGoodbye()
{
Console.WriteLine("Goodbye!");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
这个示例使用 switch 语句根据用户输入的命令执行相应的操作。虽然在这个简单的示例中 switch 语句看起来并不复杂,但随着命令数量的增加,switch 语句可能会变得臃肿且难以维护。在稍微复杂的情况下,使用委托和字典等方法会更加灵活和可扩展。
下面用委托来实现:
// 定义一个委托,它可以指向执行不同命令的方法
public delegate void CommandHandler();
class Program
{
static void Main(string[] args)
{
// 通过命令名称将委托和方法关联起来
Dictionary<string, CommandHandler> commandDictionary = new Dictionary<string, CommandHandler>
{
{ "sayHello", SayHello },
{ "sayGoodbye", SayGoodbye }
};
// 模拟用户输入的命令
string userInput = "sayHello";
// 查找并执行用户输入命令对应的方法
if (commandDictionary.ContainsKey(userInput))
{
CommandHandler handler = commandDictionary[userInput];
handler();
}
else
{
Console.WriteLine("Invalid command.");
}
}
// 不同命令对应的方法
static void SayHello()
{
Console.WriteLine("Hello!");
}
static void SayGoodbye()
{
Console.WriteLine("Goodbye!");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
在这个示例中,我们使用委托 CommandHandler 来定义执行不同命令的方法。然后,我们使用一个 Dictionary 将命令名称和对应的方法关联起来。当用户输入命令时,我们查找字典并执行相应的方法。这种方法相比于 switch 语句更加灵活,因为我们可以轻松地添加新的命令和对应的操作,而不必修改大的 switch 语句。
# Action
在 C# 中,Action 是一个泛型委托类型,它可以引用不返回值的方法。Action 委托可以引用最多 16 个参数的方法,且不返回任何值。它是一个预定义的委托类型,用于表示不带返回值的方法。 Action 委托通常用于表示需要执行某些操作但不需要返回结果的方法。它可以用于许多场景,例如事件处理、异步编程中的回调函数等。
以下是 Action 委托的一些示例用法:
class Program
{
static void Main(string[] args)
{
// 使用 Action 委托引用一个不带参数的方法
Action method1 = SayHello;
method1(); // 输出:Hello!
// 使用 Action 委托引用一个带参数的方法
Action<string> method2 = SayMessage;
method2("Hi there"); // 输出:Hi there
// 使用 Action 委托引用一个带多个参数的方法
Action<string, int> method3 = DisplayInfo;
method3("John", 30); // 输出:Name: John, Age: 30
}
static void SayHello()
{
Console.WriteLine("Hello!");
}
static void SayMessage(string message)
{
Console.WriteLine(message);
}
static void DisplayInfo(string name, int age)
{
Console.WriteLine("Name: " + name + ", Age: " + age);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
在这个示例中,我们使用了不同的 Action 委托实例来引用不同的方法,包括不带参数、带一个参数和带多个参数的方法。
# Func
在 C# 中,Func 是另一个泛型委托类型,它可以引用带有返回值的方法。与 Action 不同,Func 委托可以引用带有返回值的方法,其最后一个类型参数表示方法的返回类型。Func 委托也可以引用最多 16 个参数的方法。
以下是 Func 委托的一些示例用法:
class Program
{
static void Main(string[] args)
{
// 使用 Func 委托引用一个不带参数且返回字符串的方法
Func<string> method1 = GetGreeting;
Console.WriteLine(method1()); // 输出:Hello!
// 使用 Func 委托引用一个带一个参数且返回整数的方法
Func<int, int> method2 = DoubleNumber;
Console.WriteLine(method2(5)); // 输出:10
// 使用 Func 委托引用一个带多个参数且返回布尔值的方法
Func<string, string, bool> method3 = AreEqual;
Console.WriteLine(method3("hello", "hello")); // 输出:True
}
static string GetGreeting()
{
return "Hello!";
}
static int DoubleNumber(int num)
{
return num * 2;
}
static bool AreEqual(string str1, string str2)
{
return str1 == str2;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
在这个示例中,我们使用了不同的 Func 委托实例来引用不同的方法,包括不带参数且返回字符串、带一个参数且返回整数、带多个参数且返回布尔值的方法。
# 多播委托
多播委托是一种特殊的委托,它可以持有对一个或多个方法的引用。当多播委托调用时,它将依次调用其引用的每个方法。 在 C# 中,多播委托是通过将委托的多个实例合并成一个链来实现的。您可以使用 += 运算符将一个委托实例连接到另一个委托实例,也可以使用 -= 运算符将一个委托实例从另一个委托实例链中移除。 以下是一个简单的示例,演示了多播委托的用法:
class Program
{
public delegate void MyDelegate(string message);
static void Main(string[] args)
{
// 实例化委托,将其指向两个不同的方法
MyDelegate delegateInstance1 = new MyDelegate(Method1);
delegateInstance1 += Method2;
// 将两个委托实例连接成一个多播委托
//MyDelegate multicastDelegate = delegateInstance1 + delegateInstance2;
// 调用多播委托,它将依次调用其引用的每个方法
delegateInstance1("delegateInstance1 delegate is called!");
// 从多播委托链中移除一个委托实例
delegateInstance1 -= Method2;
// 再次调用多播委托,它只会调用仍然在链中的委托实例
delegateInstance1("After removing one delegate from delegateInstance1 delegate!");
}
static void Method1(string message)
{
Console.WriteLine("Method 1: " + message);
}
static void Method2(string message)
{
Console.WriteLine("Method 2: " + message);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
在 C# 中,多播委托的执行顺序是按照连接顺序来执行的,即先连接的方法会先被调用,后连接的方法会后被调用。 例如,如果你有一个多播委托 delegateInstance,它连接了两个方法 Method1 和 Method2,那么当你调用 delegateInstance() 时,首先会执行 Method1,然后执行 Method2。
移除委托实例的顺序与连接相反。使用 -= 运算符从多播委托中移除委托实例时,是从后往前,逐个匹配,如果匹配不到,就不做任何操作,如果匹配到,就把当前这个移除,且停止去继续往后匹配,在移除的方法的时候,必须是同一个实例的同一个方法才能移除,每个lambda表达式在底层会生成不同的方法名的,看起来一样实际不同。
# 事件(Event)
在 C# 中,事件是一种特殊的委托,用于实现发布者/订阅者模式。它允许一个对象(称为发布者)通知其他对象(称为订阅者或观察者)在特定的情况下发生了某些事情。
事件通常用于实现对象之间的松耦合,使得发布者不需要知道哪些对象正在监听它的事件,而订阅者也不需要知道事件是如何触发的。
在 C# 中,事件的声明如下:
public event EventHandler MyEvent;
这里 MyEvent 是事件的名称,EventHandler 是一个委托类型,它是 System.EventHandler 类型的实例,其定义如下:
public delegate void EventHandler(object sender, EventArgs e);
EventHandler 委托是一种通用的事件处理委托,它接受两个参数:一个 object 类型的参数 sender,用于指示事件的来源,以及一个 EventArgs 类型的参数 e,用于携带关于事件的信息。通常,如果事件不需要传递额外的信息,可以使用 EventArgs.Empty。
事件的使用通常包括以下几个步骤:
1、声明事件:在类中声明事件。 2、触发事件:在特定的情况下,使用事件的 Invoke 方法或简化语法 EventName() 来触发事件。 3、订阅事件:在其他类中订阅该事件,即将事件处理方法连接到事件上。 4、事件处理:当事件被触发时,订阅的事件处理方法将被调用。
以下是一个简单的示例,演示了事件的使用:
class Program
{
public event EventHandler MyEvent;
static void Main(string[] args)
{
Program program = new Program();
// 订阅事件
program.MyEvent += HandleEvent;
// 触发事件
program.OnMyEvent();
}
// 触发事件的方法
protected virtual void OnMyEvent()
{
MyEvent?.Invoke(this, EventArgs.Empty);
}
// 事件处理方法
static void HandleEvent(object sender, EventArgs e)
{
Console.WriteLine("Event handled!");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
在这个示例中,我们首先声明了一个名为 MyEvent 的事件。然后,在 Main 方法中,我们订阅了这个事件,并在程序中调用 OnMyEvent 方法来触发事件。当事件被触发时,事件处理方法 HandleEvent 将被调用。