引言
在 C# 编程当中,委托(Delegate)是一种特殊的类型,它允许将方法作为参数传递给其他方法,或者将方法作为返回值返回,这种特性使得委托成为实现回调函数、事件处理等,所有的委托都派生自System.Delegate类。
C#中的委托类似于C 或 C++中函数的指针。委托是存在对某个方法的引用的一种引用类型变量,引用可以在运行的时候被打断,当事件出现,就必然有委托的影子,而有委托的时候却不一定会有事件出现。
委托的概念
委托是一种引用类型变量,存储某一个方法的内存地址
委托的格式
public delegate 返回值类型 MyDelegate();
//这里面的Mydelegate就是一个委托类型
委托的使用步骤
- 声明一个委托(函数指针)
- 创建委托对象
- 创造符合委托格式的函数。(指针指向的函数)
- 将函数名称赋值给委托。
这里举个例子吧:
using System;
namespace 练习
{
internal class Program
{
public delegate int Count(int a, int b);
public static int Add(int a, int b)
{
return a + b;
}
static void Main(string[] args)
{
Count c1 = new Count(Add);
int c = c1(1, 2);
Console.WriteLine(c);
Console.Read();
}
}
}
在这串代码里面 定义了一个名为 "Count" 的委托类型,这里 Count 可以引用任何接收两个int参数并且返回一个 int 的方法
定义了一个名为 "Add" 的静态方法,它接受了两个 int 参数并且返回它们的和
在Main方法里面,创建了一个Count类型的委托实例 c1 ,并将其初始化引用为 Add 方法,并传入了这两个参数
调用委托 c1,传入两个参数1和2,由于委托引用 Add 方法,所以这行代码实际上调用了Add方法,并传入了这两个参数
小贴士
使用委托时,我们一般采用两种情况
- 直接调用委托变量,就像刚才例子里面的那样
- 使用 invoke() ,简单就直接上代码例子:
委托的用处(配有代码例子)
-
回调:
委托是实现回调的基础,它允许程序将一个方法(或函数)作为参数传递给另一个方法,然后在某个事件发生时,由接收方法调用这个方法
using System;
namespace 练习
{
// 定义一个名为Button的类,用于模拟一个按钮的行为
class Button
{
// 定义一个委托类型MyButton
public delegate void MyButton();
// ClickButton是一个委托类型的成员变量,用于存储按钮点击时的回调方法
public MyButton ClickButton;
public void Click()
{
Console.WriteLine("准备点击按钮");
if (ClickButton != null)
{
ClickButton();
}
}
}
internal class Program
{
static void MyClick()
{
Console.WriteLine("按钮被点击了!");
}
static void Main(string[] args)
{
// 创建Button类的一个实例
Button button = new Button();
// 将ClickButton委托设置为引用MyClick方法,这样点击按钮时会调用MyClick
button.ClickButton = new Button.MyButton(MyClick);
// 模拟点击按钮,这将触发ClickButton委托,调用MyClick方法
button.Click();
Console.Read();
}
}
}
-
事件处理
事件是基于委托实现的。事件允许对象在发生特定行为时通知其他对象,而委托提供了一种机制来注册和触发这些通知
-
多播委托
即一个委托可以引用多个方法。这使得你可以将多个方法链接在一起,以便它们可以依次或同时被调用。委托对象可使用 "+" 运算符进行合并。一个合并委托调用它所合并的两个委托。只有相同类型的委托可被合并。"-" 运算符可用于从合并的委托中移除组件委托。
using System;
namespace 委托的多播
{
internal class Program
{
// 定义一个委托类型CalculateDelegate,它可以引用接受两个int参数并返回int的方法
public delegate int CalculateDelegate(int x, int y);
// Add方法实现了CalculateDelegate委托的签名,用于加法运算
public static int Add(int x, int y)
{
// 输出加法运算的结果
Console.WriteLine(x + y);
return x + y;
}
// Multiply方法实现了CalculateDelegate委托的签名,用于乘法运算
public static int Multiply(int x, int y)
{
// 输出乘法运算的结果
Console.WriteLine(x * y);
return x * y;
}
static void Main(string[] args)
{
// 创建CalculateDelegate委托的实例cal0,引用Add方法
CalculateDelegate cal0 = new CalculateDelegate(Add);
// 创建CalculateDelegate委托的实例cal1,引用Multiply方法
CalculateDelegate cal1 = new CalculateDelegate(Multiply);
// 通过委托的加法运算创建一个多播委托cal,它将依次执行cal0和cal1引用的方法
CalculateDelegate cal = cal0 + cal1;
// 调用多播委托cal,传入1和3作为参数
// 这将依次调用Add和Multiply方法,并输出结果
cal(1, 3);
Console.ReadKey();
}
}
}
-
泛型委托
以引用具有泛型参数的方法,从而提供类型安全和灵活性
using System;
// 定义一个泛型委托
public delegate R MyFunc<T, R>(T t);
class Program
{
// 一个接受整数并返回字符串的方法
static string ConvertToString(int number)
{
return number.ToString();
}
static void Main()
{
// 创建泛型委托的实例,引用一个方法
MyFunc<int, string> toString = ConvertToString;
// 使用委托调用方法
string result = toString(123);
Console.WriteLine(result); // 输出 "123"
// 可以引用不同类型的方法
MyFunc<double, int> round = Math.Round;
int roundedResult = round(3.14159); // 调用 Math.Round 方法
Console.WriteLine(roundedResult); // 输出 "3"
}
}
-
方法的抽象表示
委托允许你将方法视为一个对象,这使得方法可以像其他对象一样被传递、存储和操作
using System;
namespace MethodAbstractionExample
{
// 定义一个委托类型,它抽象表示一个方法,该方法接受两个int参数并返回一个int结果
public delegate int Operation(int x, int y);
class Program
{
// 这是一个具体的加法方法,符合委托的签名
public static int Add(int a, int b)
{
return a + b;
}
// 这是一个具体的减法方法,也符合委托的签名
public static int Subtract(int a, int b)
{
return a - b;
}
static void Main(string[] args)
{
// 创建委托实例,将加法方法作为抽象方法引用
Operation operation = new Operation(Add);
int result = operation(5, 3); // 调用抽象方法,实际执行加法
Console.WriteLine($"结果是: {result}"); // 输出 8
// 可以改变委托引用的方法,以抽象表示不同的方法
operation = new Operation(Subtract);
result = operation(5, 3); // 这次实际执行的是减法
Console.WriteLine($"结果是: {result}"); // 输出 2
// 等待用户输入,以便查看控制台输出
Console.ReadLine();
}
}
}
-
匿名方法和 Lambda 表达式
public delegate void MyDelegate(string message);
public class Program
{
public static void Main()
{
MyDelegate del = delegate (string msg)
{
Console.WriteLine(msg);
};
del("!");
}
}
public delegate void MyDelegate(string message);
public class Program
{
public static void Main()
{
MyDelegate del = (msg) => Console.WriteLine(msg);
del("!");
}
}
为什么使用委托
委托是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用。与 C 或 C++ 中的函数指针不同,委托是类型安全的,并且是面向对象的。委托不仅能够实现代码的重用,还能促进逻辑的解耦,从而提高代码的可维护性和可扩展性。
其他委托形式
Action
Action
表示无参数且无返回值的委托。Action<int, string>
是一个带有参数且无返回值的泛型委托,最多支持16个参数。Action
可以涵盖所有无返回值的委托,是对无返回值委托的一个统一封装。
using System;
public class Program
{
static void DisplayMessage()
{
Console.WriteLine("Hello, World!");
}
static void DisplayMessage(string message)
{
Console.WriteLine(message);
}
public static void Main()
{
// 无参数 Action
Action actionWithoutParams = DisplayMessage;
actionWithoutParams(); // 输出 "Hello, World!"
// 带参数 Action
Action<string> actionWithParams = DisplayMessage;
actionWithParams("Hello!"); // 输出 "Hello!"
}
}
Func
Func
是一种表示有返回值的委托,它必须返回一个值。Func
可以无参数,也可以接受最多16个参数,其中最后一个参数指定返回值的类型。Func
的使用方式与delegate
相同,但它专门用于处理有返回值的情况,为这些委托提供了一个统一的封装。
using System;
public class Program
{
// Func 示例,接受两个int参数,返回它们的和
static int Sum(int x, int y)
{
return x + y;
}
public static void Main()
{
// 创建 Func 委托实例
Func<int, int, int> sumFunc = Sum;
// 调用 Func 委托
int result = sumFunc(5, 10); // 返回 15
Console.WriteLine($"result: {result}");
}
}
Predicate<T>
用于表示接受一个类型为 T
的参数并返回 bool
的方法。我们展示了如何使用筛选数组中的偶数
using System;
using System.Linq;
public class Program
{
public static void Main()
{
// 创建一个整数数组
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// 使用 Predicate<T> 来筛选数组中的偶数
Predicate<int> isEven = number => number % 2 == 0;
// 筛选出所有偶数
int[] evenNumbers = numbers.Where(isEven).ToArray();
// 输出筛选结果
Console.WriteLine("numbers:");
foreach (var number in evenNumbers)
{
Console.WriteLine(number);
}
}
}