目录

C-委托使用详解

C# 委托使用详解


前言

在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 创建委托实例, 必须 传入一个方法作为参数,否则会报错

https://i-blog.csdnimg.cn/direct/e4d1f25ead1348eaa8b0dd0bd90682b5.png

// 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

可以看到,方法 AddSubtract 都被依次调用了,由于 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> 在 此处 FindWhere 中的作用就是筛选出符合条件的数据。当数据符合条件,返回 true ,将符合条件的数据返回或加入到新的序列中,返回新的序列。

3)小结

  • Action<T> 表示一个没有返回值的委托, Action<T> 相当于是对所有 无返回值委托的进一步包装
  • Func<T> 表示一个有返回值的委托, Func<T> 相当于是对所有 有返回值委托的进一步包装
  • Action<T>Func<T> 内置委托基本已经涵盖了所有的委托使用场景,除非有特殊需求,否则建议直接使用这些内置委托,而不是重新定义新的委托类型。
  • Func<T> 表示一个返回 bool 的委托, Func<T> 相当于是对所有 返回 bool 委托的进一步包装

3. 异步委托

委托支持异步调用,可以使用 BeginInvokeEndInvoke 方法进行异步操作。

  • BeginInvoke 开启一个线程去执行委托
  • BeginInvokeEndInvoke 方法 仅 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# 提供了 ActionFuncPredicate 类型,用于表示常见的委托类型。在大多数情况下,建议使用 ActionFunc 类型,而不是自定义委托类型。

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#中,推荐使用 asyncawait 来实现异步操作。这种方式更加直观和易于维护。

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
    }
}

结语

回到目录页:

希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。


参考资料: