总目录
前言
在C#中,委托(Delegate) 是一种类型安全的函数指针机制,它允许我们将方法作为参数传递给其他方法,或者将方法存储在变量中。委托在 C# 中有广泛的应用,特别是在事件处理、异步编程和回调机制中。本文将详细介绍 C# 中委托的定义、使用方法、高级特性以及最佳实践。
一、什么是委托
1. 定义
- 委托是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用。
- 委托是一种特殊的类,它封装了一个或多个具有相同签名(返回类型和参数列表)的方法。它允许我们将方法作为参数传递给其他方法,或者将方法存储在变量中。 委托可以看作是方法的“蓝图”,它定义了方法的签名(返回类型和参数列表),但不包含方法体。
- 委托是一种类型安全的函数指针,用于封装对方法的引用。它允许将方法作为参数传递、存储在变量中,或通过多播机制调用多个方法。
2. 特点
- 类型安全:委托的签名(参数列表和返回类型)必须与目标方法签名完全匹配。
- 面向对象:委托本质上是继承自
System.MulticastDelegate
的类,支持多态和反射操作。 - 动态调用:运行时根据条件动态选择要调用的方法。
- 多播能力:支持多个方法的链式调用
- 异步支持:通过
BeginInvoke
/EndInvoke
实现异步调用
3. 委托定义语法
委托是一种引用类型,它指向具有特定参数列表和返回类型的方法。委托的定义语法如下:
public delegate ReturnType DelegateName(Parameters);
ReturnType
:委托方法的返回类型。Parameters
:委托方法的参数列表。DelegateName
:委托的名称。
二、如何使用委托
1. 使用步骤
1)定义委托
- 定义委托的方式类似于定义方法
- 相较于定义方法,定义委托需要使用
delegate
关键字,且没有方法体。
- 相较于定义方法,定义委托需要使用
- 委托本质上也是类,因此可定义的位置与类相同。
- 类可以定义在哪里,委托就可定义在哪里。
public delegate int MathOperationDelegate(int a, int b);
2)实例化委托
可以通过以下几种方式实例化委托:
- new 实例化:通过
new
创建委托实例 - 直接赋值 :直接赋值给某个方法
- 匿名方法 :使用匿名方法
- Lambda 表达式:使用 Lambda 表达式
方式1:new 实例化
// 实例化委托:new 实例化
MathOperationDelegate mathOperation = new MathOperationDelegate(Add);
通过new 创建委托实例,必须传入一个方法作为参数,否则会报错
// 1. 定义委托
public delegate int MathOperationDelegate(int a, int b);
class Program
{
static void Main()
{
// 3.实例化委托
MathOperationDelegate mathOperation = new MathOperationDelegate(Add);
}
// 2.定义委托对应的方法
// 注意:该方法的签名必须与定义的委托签名保持一致
public static int Add(int num1, int num2)
{
return num1 + num2;
}
}
方式2:直接赋值
C#2.0 提供一个更简单的给委托创建实例的方式,那就是直接赋值。
// 实例化委托:直接赋值,将Add 方法赋值给定义的委托
MathOperationDelegate mathOperation = Add;
// 1. 定义委托
public delegate int MathOperationDelegate(int a, int b);
class Program
{
static void Main()
{
// 3.实例化委托:直接赋值,将Add 方法赋值给定义的委托
MathOperationDelegate mathOperation = Add;
}
// 2.定义委托对应的方法
// 注意:该方法的签名必须与定义的委托签名保持一致
public static int Add(int num1, int num2)
{
return num1 + num2;
}
}
方式3:匿名方法
从C# 2.0开始,可以使用匿名方法来创建委托实例。匿名方法是在创建委托实例时定义的方法体,不需要单独命名。
// 实例化委托: 使用匿名方法,可在不定义命名方法的情况下直接编写方法体
MathOperationDelegate mathOperation = delegate (int num1, int num2)
{
return num1 + num2;
};
// 1. 定义委托
public delegate int MathOperationDelegate(int a, int b);
class Program
{
static void Main()
{
// 2.实例化委托: 使用匿名方法,可在不定义命名方法的情况下直接编写方法体
MathOperationDelegate mathOperation = delegate (int num1, int num2)
{
return num1 + num2;
};
}
}
方式4:Lambda 表达式
从C# 3.0开始,可以使用Lambda表达式来创建委托实例。Lambda表达式是一种更简洁的匿名方法语法。
// 实例化委托: 使用 Lambda 表达式
MathOperationDelegate mathOperation = (x, y) => x + y;
// 1. 定义委托
public delegate int MathOperationDelegate(int a, int b);
class Program
{
static void Main()
{
// 2.实例化委托: 使用 Lambda 表达式
MathOperationDelegate mathOperation = (x, y) => x + y;
}
}
3)使用委托
通过调用委托,就可以间接的调用委托上对应的方法。
方式1:调用委托变量
传入对应的参数,像调用方法一样调用委托。
mathOperation(4, 5)
public delegate int MathOperationDelegate(int a, int b);
class Program
{
static void Main()
{
MathOperationDelegate mathOperation = (x, y) => x + y;
Console.WriteLine(mathOperation(4, 5)); //输出:9
}
}
方式2:Invoke 调用
使用Invoke
方法,调用委托。使用Invoke
调用委托,如果定义的委托没有参数,则invoke也没有参数,如果定义的委托没有返回值,则invoke也没有返回值。
mathOperation.Invoke(4, 5)
public delegate int MathOperationDelegate(int a, int b);
class Program
{
static void Main()
{
MathOperationDelegate mathOperation = (x, y) => x + y;
Console.WriteLine(mathOperation.Invoke(4, 5)); //输出:9
}
}
2. 示例
1)完整示例代码
// 1. 定义委托
public delegate int MathOperationDelegate(int a, int b);
class Program
{
// 2.定义委托对应的方法
// 注意:该方法的签名必须与定义的委托签名保持一致
public static int Add(int num1, int num2)
{
return num1 + num2;
}
static void Main()
{
// 3.实例化委托
// 实例化方式 1
MathOperationDelegate mathOperation1 = new MathOperationDelegate(Add);
// 实例化方式 2
MathOperationDelegate mathOperation2 = Add;
// 实例化方式 3
MathOperationDelegate mathOperation3 = delegate (int x, int y)
{
return x + y;
};
// 实例化方式 4
MathOperationDelegate mathOperation4 = (x, y) => x + y;
//4. 调用委托
// 调用方式 1
var reselt1 = mathOperation1(4, 5);
Console.WriteLine(reselt1); //输出:9
// 调用方式 2
var reselt2 = mathOperation4.Invoke(4, 5);
Console.WriteLine(reselt2); //输出:9
}
}
2)委托基本使用示例
// 1. 定义委托
public delegate int MathOperationDelegate(int a, int b);
class Program
{
static void Main()
{
// 2. 实例化委托
MathOperationDelegate mathOperation = (x, y) => x + y;
// 3. 调用委托
Console.WriteLine(mathOperation.Invoke(4, 5)); //输出:9
}
}
3. 为什么使用委托
- 解耦代码:分离方法定义与调用逻辑,例如事件处理、异步回调。
- 实现回调机制:委托提供了灵活的回调机制,可以在方法执行完成时触发特定的操作。
- 提高代码复用性:通过委托,可以将通用的处理逻辑提取出来,减少重复代码。
- 灵活性和扩展性:委托允许你在运行时动态地指定要调用的方法,使得代码更加灵活和易于扩展。
- 异步编程支持:委托支持异步编程模型,使得你可以轻松地编写异步操作。
- 支持事件驱动:通过事件封装委托,实现发布-订阅模式
三、委托的特性
1. 多播委托
1)基本信息
委托可以关联多个方法,形成多播委托。多播委托通过 +=
或 -=
操作符来添加或移除关联方法,形成一个委托链。当调用这个委托链时,所有关联的方法按添加的顺序依次执行。
2)多播委托特点
- 委托链:多播委托可以看作是一个委托链,其中每个节点代表一个方法。
- 操作符支持:通过
+=
操作符添加方法,通过-=
操作符移除方法。 - 调用顺序:当调用一个多播委托时,会按照它们被添加的顺序依次调用每个方法。
- 返回值:如果委托有返回值,只有最后一个方法的返回值会被保留,其他返回值会被丢弃。
- 异常处理:如果其中一个方法抛出异常,则后续的方法将不会被调用,除非使用特殊的方式处理异常(如捕获异常并继续执行)。
3)使用示例
为了更好地理解多播委托的工作原理,我们来看一个具体的示例。
首先,定义一个简单的委托类型,并编写几个方法:
using System;
// 定义一个委托类型
public delegate void MathOperation(int a, int b);
public class Calculator
{
public void Add(int a, int b)
{
Console.WriteLine($"Add: {a} + {b} = {a + b}");
}
public void Subtract(int a, int b)
{
Console.WriteLine($"Subtract: {a} - {b} = {a - b}");
}
public void Multiply(int a, int b)
{
Console.WriteLine($"Multiply: {a} * {b} = {a * b}");
}
}
接下来,我们将这些方法绑定到一个多播委托实例中,并依次调用它们:
public class Program
{
static void Main()
{
Calculator calculator = new Calculator();
// 创建委托实例并绑定方法
MathOperation operation = calculator.Add;
operation += calculator.Subtract;
operation += calculator.Multiply; //添加方法
operation -= calculator.Multiply; //移除方法
// 调用多播委托
operation(5, 3);
}
}
当你运行上述代码时,输出将是:
Add: 5 + 3 = 8
Subtract: 5 - 3 = 2
可以看到,方法 Add
、Subtract
都被依次调用了,由于Multiply
方法使用-=
移除,所以没被调用 。
4)获取委托链中的方法
有时你可能需要获取多播委托链中的所有方法,以便进行进一步的处理或调试。可以通过 GetInvocationList()
方法来实现这一点。
public class Program
{
static void Main()
{
Calculator calculator = new Calculator();
// 创建委托实例并绑定方法
MathOperation operation = calculator.Add;
operation += calculator.Subtract;
operation += calculator.Multiply;
// 获取委托链中的所有方法
Delegate[] delegates = operation.GetInvocationList();
foreach (MathOperation method in delegates)
{
Console.WriteLine($"Calling method: {method.Method.Name}");
method(5, 3);
}
}
}
输出结果
Calling method: Add
Add: 5 + 3 = 8
Calling method: Subtract
Subtract: 5 - 3 = 2
Calling method: Multiply
Multiply: 5 * 3 = 15
5)返回值处理
多播委托的返回值是最后一个被调用的方法的返回值。如果你希望获取所有方法的返回值,可以通过遍历委托链中的方法逐一调用并收集结果。
假设我们有一个带返回值的委托类型:
public delegate int MathOperationWithReturn(int a, int b);
public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
public int Subtract(int a, int b)
{
return a - b;
}
public int Multiply(int a, int b)
{
return a * b;
}
}
public class Program
{
static void Main()
{
Calculator calculator = new Calculator();
// 创建委托实例并绑定方法
MathOperationWithReturn operation = calculator.Add;
operation += calculator.Subtract;
operation += calculator.Multiply;
// 调用多播委托并获取最后一个方法的返回值
int result = operation(5, 3);
Console.WriteLine($"Last method's result: {result}");
// 获取所有方法的返回值
Delegate[] delegates = operation.GetInvocationList();
foreach (MathOperationWithReturn method in delegates)
{
int tempResult = method(5, 3);
Console.WriteLine($"{method.Method.Name} returned: {tempResult}");
}
}
}
输出结果
Last method's result: 15
Add returned: 8
Subtract returned: 2
Multiply returned: 15
6)异常处理
如果多播委托链中的某个方法抛出异常,默认情况下后续的方法将不会被调用。为了避免这种情况,可以在调用每个方法时捕获异常并继续执行。
public class Program
{
static void Main()
{
Calculator calculator = new Calculator();
// 创建委托实例并绑定方法
MathOperation operation = calculator.Add;
operation += ThrowException; // 这个方法会抛出异常
operation += calculator.Multiply;
// 调用多播委托并处理异常
InvokeAll(operation, 5, 3);
}
public static void ThrowException(int a, int b)
{
throw new InvalidOperationException("An exception occurred!");
}
public static void InvokeAll(MathOperation operation, int a, int b)
{
Delegate[] delegates = operation.GetInvocationList();
foreach (MathOperation method in delegates)
{
try
{
Console.WriteLine($"Calling method: {method.Method.Name}");
method(a, b);
}
catch (Exception ex)
{
Console.WriteLine($"Caught exception in {method.Method.Name}: {ex.Message}");
}
}
}
}
输出结果
Calling method: Add
Add: 5 + 3 = 8
Calling method: ThrowException
Caught exception in ThrowException: An exception occurred!
Calling method: Multiply
Multiply: 5 * 3 = 15
2. 泛型委托
1)自定义泛型委托
// 1. 定义委托
public delegate T MathOperationDelegate<T>(T a, T b);
class Program
{
static void Main()
{
// 2. 实例化委托
MathOperationDelegate<int> mathOperation1 = (x, y) => x + y;
MathOperationDelegate<double> mathOperation2 = (x, y) => x - y;
// 3. 调用委托
Console.WriteLine(mathOperation1.Invoke(4, 5)); //输出:9
Console.WriteLine(mathOperation2.Invoke(45.5, 25.1)); //输出:20.4
}
}
建议:C#
提供了一些内置的泛型委托类型,如 Func<T>
、Action<T>
和 Predicate<T>
,这些内置委托已经涵盖了大多数常见的场景。除非有特殊需求,否则建议直接使用这些内置委托,而不是重新定义新的委托类型。
2)内置通用委托
C# 提供了一些内置的泛型委托类型,如 Func<T>
和 Action<T>
、Predicate<T>
,简化了委托的定义和使用。
对比表格
特性 | Action<T> 系列 | Func<T> 系列 | Predicate<T> |
---|---|---|---|
定义 | 表示一个没有返回值的委托 | 表示一个有返回值的委托 | 表示一个返回bool 的委托 |
泛型参数数量 | 0到16个输入参数, 无返回值 | 0到16个输入参数, 最后一个表示返回值且只有一个 | 固定1个输入参数, 返回布尔值 |
返回值类型 | void | 可以是任意类型 | bool |
使用场景 | 执行某些操作但不需要返回结果 | 执行某些计算或处理并返回结果 | 判断某个条件是否满足 |
常见用法 | 处理事件、执行副作用操作 | 计算、查询、转换等需要返回结果的操作 | 过滤集合元素、验证条件 |
Action<T>
系列
- 定义:
Action<T>
是一个不带返回值的委托类型,可以接受0到16个输入参数。 - 泛型参数:
Action
本身表示无参数的委托,Action<T>
表示接受一个参数的委托,依此类推,最多可以接受16个参数。 - 返回值:
void
,即没有返回值。 - 典型应用:
- 执行某些操作,如日志记录、文件写入等。
- 处理事件回调函数。
public class Program
{
static void Main()
{
//实例化方式 1
Action action = new Action(Show);
action();
//实例化方式 2
action = Show;
action();
//实例化方式 3
action = delegate () { Console.WriteLine("hello"); };
action();
//实例化方式 4
// Action 无参数
Action log = () => Console.WriteLine("Logging...");
log();
// Action<T> 单个参数
Action<string> printMessage = message => Console.WriteLine(message);
printMessage("Hello, World!");
// Action<T1, T2> 多个参数
Action<int, int> addAndPrint = (a, b) => Console.WriteLine(a + b);
addAndPrint(5, 3);
}
public static void Show()
{
Console.WriteLine("hello world!");
}
}
Func<T>
系列
- 定义:
Func<T>
是一个带有返回值的委托类型,可以接受0到16个输入参数,并且必须有一个返回值。 - 泛型参数:
Func<TResult>
表示无参数但有返回值的委托,Func<T, TResult>
表示接受一个参数并返回结果的委托,依此类推,最多可以接受16个参数。 - 返回值:最后一个泛型参数指定返回值的类型。
- 典型应用:
- 执行计算、查询、转换等需要返回结果的操作。
- 数据处理和映射。
public class Program
{
static void Main()
{
// Func<TResult> 无参数但有返回值
Func<int> getRandomNumber = () => new Random().Next(1, 100);
Console.WriteLine(getRandomNumber());
// Func<T, TResult> 单个参数
Func<int, int> square = x => x * x;
Console.WriteLine(square(5));
// Func<T1, T2, TResult> 多个参数
Func<int, int, int> add = (a, b) => a + b;
Console.WriteLine(add(5, 3));
}
}
Predicate<T>
- 定义:
Predicate<T>
是一个返回布尔值的委托类型,专门用于判断某个条件是否满足。它只接受一个输入参数。 - 泛型参数:固定为一个参数类型。
- 返回值:
bool
,用于表示条件是否成立。 - 典型应用:
- 集合过滤、元素验证等场景。
- 条件判断。
public class Program
{
static void Main()
{
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
// Predicate<T> 判断数字是否大于3
Predicate<int> isGreaterThanThree = n => n > 3;
// 使用 Find 方法查找第一个符合条件的元素
int result = numbers.Find(isGreaterThanThree);
Console.WriteLine(result); // 输出: 4
// 使用 Where 方法过滤所有符合条件的元素
var filteredNumbers = numbers.Where(n => isGreaterThanThree(n));
foreach (var num in filteredNumbers)
{
Console.WriteLine(num); // 输出: 4, 5
}
}
}
Predicate<int> isGreaterThanThree = n => n > 3;
// 使用 Find 方法查找第一个符合条件的元素
int result = numbers.Find(isGreaterThanThree);
//等效于
int result = numbers.Find(x => x > 3);
var filteredNumbers = numbers.Where(n => isGreaterThanThree(n));
//等效于
var filteredNumbers = numbers.Where(x => x > 3);
Predicate<T>
在 此处Find
和 Where
中的作用就是筛选出符合条件的数据。当数据符合条件,返回true
,将符合条件的数据返回或加入到新的序列中,返回新的序列。
3)小结
Action<T>
表示一个没有返回值的委托,Action<T>
相当于是对所有 无返回值委托的进一步包装Func<T>
表示一个有返回值的委托,Func<T>
相当于是对所有 有返回值委托的进一步包装Action<T>
和Func<T>
内置委托基本已经涵盖了所有的委托使用场景,除非有特殊需求,否则建议直接使用这些内置委托,而不是重新定义新的委托类型。Func<T>
表示一个返回bool
的委托,Func<T>
相当于是对所有 返回bool
委托的进一步包装
3. 异步委托
委托支持异步调用,可以使用 BeginInvoke
和 EndInvoke
方法进行异步操作。
BeginInvoke
开启一个线程去执行委托BeginInvoke
和EndInvoke
方法 仅 NetFamework支持 ,NetCore中更推荐async/await
using System;
using System.Reflection;
public delegate int MathOperation(int a, int b);
public class Calculator
{
public int Add(int a, int b)
{
Console.WriteLine(MethodBase.GetCurrentMethod().Name);
return a + b;
}
public int Subtract(int a, int b)
{
Console.WriteLine(MethodBase.GetCurrentMethod().Name);
return a - b;
}
}
class Program
{
static void Main()
{
Calculator calculator = new Calculator();
MathOperation operation = calculator.Add;
// 异步调用委托
IAsyncResult result = operation.BeginInvoke(5, 3, null, null);
// 在主线程继续执行其他操作
Console.WriteLine("Doing other work...");
// 等待异步调用完成并获取结果
int sum = operation.EndInvoke(result);
Console.WriteLine($"Sum: {sum}"); // 输出: Sum: 8
}
}
运行结果:
Doing other work...
Add
Sum: 8
如果是使用Invoke 方法同步执行:
class Program
{
static void Main()
{
Calculator calculator = new Calculator();
MathOperation operation = calculator.Add;
// 同步调用委托
int sum = operation.Invoke(5, 3);
// 继续执行其他操作
Console.WriteLine("Doing other work...");
// 输出结果
Console.WriteLine($"Sum: {sum}"); // 输出: Sum: 8
}
}
结果如下
Add
Doing other work...
Sum: 8
四、委托的最佳实践
1. 优先使用内置委托
优先使用内置委托,减少冗余代码,提升可维护性。 C# 提供了 Action
和 Func
、Predicate
类型,用于表示常见的委托类型。在大多数情况下,建议使用 Action
和 Func
类型,而不是自定义委托类型。
2. 使用 Lambda 表达式
Lambda 表达式提供了简洁的语法,用于表示匿名函数。在大多数情况下,建议使用 Lambda 表达式,而不是匿名委托或命名方法。
3. 避免过度使用委托
虽然委托非常灵活,但在某些情况下,过度使用委托可能会导致代码难以阅读和维护。在设计代码时,应权衡委托的灵活性和代码的可读性。
5. 避免复杂的委托链
虽然委托链功能强大,但过于复杂的委托链会使代码难以维护。尽量保持委托链简单明了。
4. 注意线程安全性
在多线程环境中使用委托时,需要注意线程安全性问题。确保在访问共享资源时使用适当的同步机制。
// 错误的委托调用方式
public class UnsafeInvoker
{
public Action Callback;
public void DoWork()
{
if (Callback != null) // ❌ 非原子操作
{
Callback(); // ❌ 可能已被置空
}
}
}
// 正确方式
public void SafeDoWork()
{
var localCopy = Callback;
if (localCopy != null)
{
localCopy();
}
}
5. 合理选择委托或事件
需要解耦通知机制时用事件,动态调用方法时用委托。
6. 注意闭包变量生命周期
避免意外捕获外部变量导致内存泄漏。
五、应用场景
1. 应用场景概览
- 事件处理:委托常用于事件处理机制,使得对象可以订阅和触发事件。
- 回调函数:委托可以用来实现回调函数,使得一个方法可以在另一个方法完成后被调用。
- 异步编程:委托支持异步编程模型,使得你可以轻松地编写异步操作。
- 多播委托:委托可以封装多个方法,并按顺序调用这些方法。
2. 应用场景示例
示例1:事件处理
委托是C#中事件处理的基础,委托常用于事件处理机制。以下是一个简单的事件处理示例:
public class Button
{
// 定义一个委托类型
public delegate void ClickEventHandler(object sender, EventArgs e);
// 定义一个事件
public event ClickEventHandler Click;
// 触发事件
public void OnClick()
{
Click?.Invoke(this, EventArgs.Empty);
}
}
class Program
{
static void Main()
{
Button button = new Button();
// 订阅事件
button.Click += (sender, e) =>
{
Console.WriteLine("Button clicked!");
};
// 触发事件
button.OnClick(); // 输出: Button clicked!
}
}
示例2:回调函数
委托可以用来实现回调函数,使得一个方法可以在另一个方法完成后被调用。
public class TaskManager
{
public void ExecuteTask(Action callback)
{
Console.WriteLine("Executing task...");
// 模拟任务执行
System.Threading.Thread.Sleep(1000);
Console.WriteLine("Task completed.");
// 调用回调函数
callback();
}
}
class Program
{
static void Main()
{
TaskManager taskManager = new TaskManager();
// 定义回调函数
Action callback = () => Console.WriteLine("Callback executed!");
// 执行任务并传递回调函数
taskManager.ExecuteTask(callback);
}
}
输出结果:
Executing task...
Task completed.
Callback executed!
示例3: 回调函数
委托可以用作回调函数,当某个操作完成时,调用委托来通知调用者。例如:
public delegate void MyDelegate(string message);
public class DataLoader
{
public void LoadData(MyDelegate callback)
{
// 模拟数据加载
System.Threading.Thread.Sleep(1000);
callback("Data loaded successfully!");
}
}
public class Program
{
public static void Main()
{
DataLoader loader = new DataLoader();
loader.LoadData(DisplayMessage);
}
public static void DisplayMessage(string message)
{
Console.WriteLine(message);
}
}
示例4:异步编程
委托支持异步编程模型,使得你可以轻松地编写异步操作。
public class AsyncOperation
{
public void PerformOperation(Func<int, int, int> operation, int a, int b)
{
Console.WriteLine("Starting asynchronous operation...");
var result = operation.BeginInvoke(a, b, null, null);
// 在主线程继续执行其他操作
Console.WriteLine("Doing other work...");
// 获取异步操作的结果
int sum = operation.EndInvoke(result);
Console.WriteLine($"Result: {sum}");
}
}
class Program
{
static void Main()
{
AsyncOperation asyncOperation = new AsyncOperation();
// 定义异步操作
Func<int, int, int> addFunc = (x, y) =>
{
Console.WriteLine("Add Operation");
return x + y;
};
// 执行异步操作
asyncOperation.PerformOperation(addFunc, 5, 3);
}
}
运行结果:
Starting asynchronous operation...
Doing other work...
Add Operation
Result: 8
示例5:异步流水线处理
虽然委托支持使用BeginInvoke
异步调用,但在现代C#中,推荐使用 async
和 await
来实现异步操作。这种方式更加直观和易于维护。
using System;
using System.Threading.Tasks;
public class Program
{
public static async Task PipelineProcessingAsync()
{
// 定义下载数据的操作
Func<string, Task<string>> download = async url => {
await Task.Delay(100); // 模拟网络延迟
return $"Data from {url}";
};
// 定义处理数据的操作
Func<string, Task<string>> process = async data => {
await Task.Delay(50); // 模拟处理时间
return data.ToUpper();
};
// 定义保存结果的操作
Func<string, Task> save = async result => {
await Task.Delay(20); // 模拟保存时间
Console.WriteLine($"Saved: {result}");
};
// 构建处理流水线
async Task ProcessPipeline(string url)
{
var downloadedData = await download(url);
var processedData = await process(downloadedData);
await save(processedData);
}
// 执行处理流水线
await ProcessPipeline("http://example.com");
}
// NetFamework 下的写法
public static void Main(string[] args)
{
PipelineProcessingAsync().GetAwaiter().GetResult();
//输出:Saved: DATA FROM HTTP://EXAMPLE.COM
}
}
NetCore下的写法
public static async void Main(string[] args)
{
await PipelineProcessingAsync();
//输出:Saved: DATA FROM HTTP://EXAMPLE.COM
}
示例6:动态方法选择器
public class CommandDispatcher
{
private readonly Dictionary<string, Action<string>> _handlers
= new Dictionary<string, Action<string>>();
public void RegisterHandler(string command, Action<string> handler)
{
_handlers[command] = handler;
}
public void Execute(string command, string parameter)
{
if (_handlers.TryGetValue(command, out var handler))
{
handler(parameter);
}
else
{
throw new InvalidOperationException($"未知命令: {command}");
}
}
}
示例7:策略模式实现
public class PaymentProcessor
{
private Func<Order, PaymentResult> _paymentStrategy;
public void SetPaymentStrategy(Func<Order, PaymentResult> strategy)
{
_paymentStrategy = strategy;
}
public PaymentResult Process(Order order)
{
return _paymentStrategy?.Invoke(order)
?? throw new InvalidOperationException("未设置支付策略");
}
}
// 使用示例
processor.SetPaymentStrategy(CreditCardStrategy);
var result = processor.Process(order);
示例8:动态方法调用
通过反射动态绑定委托,适用于插件化架构:
using System;
using System.Reflection;
public class Utility
{
// Encrypt 方法接受一个字符串参数并返回加密后的字符串
public static string Encrypt(string input)
{
// 简单的加密逻辑(实际应用中应使用更安全的加密算法)
char[] chars = input.ToCharArray();
for (int i = 0; i < chars.Length; i++)
{
chars[i] = (char)(chars[i] + 1); // 将每个字符编码值加1
}
return new string(chars);
}
}
public class Program
{
public static void Main(string[] args)
{
// 获取 Utility 类中的 Encrypt 方法
MethodInfo method = typeof(Utility).GetMethod("Encrypt", BindingFlags.Public | BindingFlags.Static);
if (method == null)
{
Console.WriteLine("未能找到 Encrypt 方法。");
return;
}
// 使用反射创建 Func<string, string> 委托实例
Delegate delegateInstance = Delegate.CreateDelegate(typeof(Func<string, string>), method);
if (delegateInstance == null)
{
Console.WriteLine("未能创建委托实例。");
return;
}
// 将 delegateInstance 转换为 Func<string, string>
Func<string, string> encryptFunc = (Func<string, string>)delegateInstance;
// 调用委托实例
string originalText = "Hello, World!";
string encryptedText = encryptFunc(originalText);
Console.WriteLine($"原始文本: {originalText}");
Console.WriteLine($"加密后的文本: {encryptedText}");
}
}
示例9:表达式树
// 传统匿名方法
MathOperation sqrt = delegate(int x) { return Math.Sqrt(x); };
// Lambda表达式
MathOperation sqrt = x => Math.Sqrt(x);
// 表达式树(编译时转换为委托)
Expression<Func<int, double>> expr = x => Math.Sqrt(x);
var compiled = expr.Compile();
public class Program
{
public static void Main()
{
Expression<Func<int, double>> expr = x => Math.Sqrt(x);
var compiled = expr.Compile();
Console.WriteLine(compiled.Invoke(16)); //输出:4
}
}
结语
回到目录页:C#/.NET 知识汇总
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。
参考资料:
Microsoft Docs: 事件
Microsoft Docs: 异步编程
Microsoft Docs: 委托文档