C# 委托中 Invoke/BeginInvoke/EndInvoke和DynamicInvoke 方法

发布于:2025-03-12 ⋅ 阅读:(130) ⋅ 点赞:(0)

总目录


前言

在C#中,委托(Delegate)提供了多种调用方式,包括 InvokeBeginInvokeEndInvokeDynamicInvoke。每种调用方式都有其特定的用途和适用场景。下面将详细介绍这些方法的区别与联系。


一、 Invoke方法

1. 定义

Invoke 是同步调用委托的方法。它会阻塞当前线程,直到委托所引用的方法执行完毕并返回结果。

2. 特点

  • 同步调用:当前线程会被阻塞,直到委托方法执行完成。
  • 简单直接:适用于不需要异步处理的场景。

3. 示例代码

public class Program
{
    public static void Main()
    {
        // 定义委托
        Action action = () => Console.WriteLine("Hello, World!");

        // 同步调用
        action.Invoke();
    }
}
public class Program
{
    public delegate string SimpleDelegate(string message);

    public static string PrintMessage(string message)
    {
        Console.WriteLine($"Message: {message}");
        return "Done";
    }

    public static void Main()
    {
        SimpleDelegate del = PrintMessage;
        string result = del.Invoke("Hello, World!"); // 同步调用
        Console.WriteLine(result);
    }
}

输出结果

Message: Hello, World!
Done

二、 BeginInvokeEndInvoke 方法

1. 定义

BeginInvokeEndInvoke 方法用于异步调用委托所引用的方法。BeginInvoke 方法启动异步操作并立即返回一个 IAsyncResult 对象,该对象可以用于跟踪异步操作的状态。而 EndInvoke 方法用于获取异步调用的结果或等待异步调用完成。

2. 特点

  • BeginInvoke
    • 异步调用:当前线程不会被阻塞,委托方法将在后台线程上执行。
    • 回调机制:可以通过提供一个回调函数,在委托方法完成后自动调用该回调函数。
    • 参数传递:除了委托方法的参数外,还需要传递一个 AsyncCallback 委托和一个用户定义的对象(通常是 null)。
  • EndInvoke
    • 获取结果:通过传入 BeginInvoke 返回的 IAsyncResult 对象来获取异步调用的结果。
    • 等待完成:如果异步调用尚未完成,EndInvoke 将阻塞当前线程,直到异步调用完成。

3. 示例代码

using System;

public class Program
{
    public delegate string SimpleDelegate(string message);

    public static string PrintMessage(string message)
    {
        Console.WriteLine($"Message: {message}");
        return "Done";
    }

    public static void Main()
    {
        SimpleDelegate del = PrintMessage;
        IAsyncResult asyncResult = del.BeginInvoke("Hello, World!", null, null); // 异步调用
        Console.WriteLine("Main method continues...");

        // 等待异步调用完成并获取结果
        string result = del.EndInvoke(asyncResult);
        Console.WriteLine(result);
    }
}

输出结果

Main method continues...
Message: Hello, World!
Done
using System;
using System.Threading;

public class Program
{
    public static void Main()
    {
        // 定义委托
        Func<int> func = () =>
        {
            Thread.Sleep(2000);
            Console.WriteLine("Asynchronous operation completed.");
            return 42;
        };

        // 异步调用
        IAsyncResult result = func.BeginInvoke(null, null);

        // 执行其他操作
        Console.WriteLine("Performing other tasks while waiting...");

        // 等待异步操作完成并获取结果
        int returnValue = func.EndInvoke(result);
        Console.WriteLine($"Return value: {returnValue}");
    }
}

三、DynamicInvoke 方法

1. 定义

DynamicInvoke 是一种动态调用委托的方法,允许以任意类型的参数调用委托,而不需要指定具体的参数类型。
Invoke 不同,DynamicInvoke 可以在运行时动态地确定要调用的方法,并且可以处理参数和返回值的类型。

2. 特点

  • 灵活性高:可以在运行时动态确定参数类型和数量。
  • 性能较低:由于需要进行类型检查和转换,性能通常低于直接调用(如 InvokeBeginInvoke)。

3. 示例代码

using System;

public class Program
{
    public delegate string SimpleDelegate(string message);

    public static string PrintMessage(string message)
    {
        Console.WriteLine($"Message: {message}");
        return "Done";
    }

    public static void Main()
    {
        SimpleDelegate del = PrintMessage;
        object[] args = new object[] { "Hello, DynamicInvoke!" };
        string result = (string)del.DynamicInvoke(args); // 动态调用
        Console.WriteLine(result);
    }
}

输出结果

Message: Hello, DynamicInvoke!
Done
using System;

public class Program
{
    public static void Main()
    {
        // 定义委托
        Func<int, int, int> add = (a, b) => a + b;

        // 动态调用
        object result = add.DynamicInvoke(2, 3);
        Console.WriteLine($"Result: {result}");	//输出:Result: 5
    }
}

四、比较与总结

1. 概览

方法 调用方式 是否阻塞当前线程 参数类型要求 性能 主要应用场景
Invoke 同步 固定 简单同步调用
BeginInvoke 异步 固定 中等 异步调用
EndInvoke 同步 固定 中等 获取异步调用结果
DynamicInvoke 动态 动态 运行时动态调用

2. 具体区别与联系

  1. Invoke vs BeginInvoke

    • Invoke 是同步调用,会阻塞当前线程直到方法执行完成;BeginInvoke 是异步调用,不会阻塞当前线程。
    • Invoke 适用于需要立即得到结果的场景;BeginInvoke 适用于需要提高响应速度、避免阻塞主线程的场景。
  2. BeginInvoke vs EndInvoke

    • BeginInvoke 用于启动异步调用,返回一个 IAsyncResult 对象;EndInvoke 用于获取异步调用的结果或等待异步调用完成。
    • 在使用 BeginInvoke 后,必须调用 EndInvoke 来确保资源释放和获取结果。
  3. DynamicInvoke

    • DynamicInvoke 提供了极大的灵活性,但代价是性能较低,因为它需要在运行时进行类型检查和转换。
    • 适用于需要在运行时动态确定参数类型和数量的场景。

3. 实际应用建议

  • 同步调用:如果你需要立即得到结果并且不关心阻塞当前线程,使用 Invoke
  • 异步调用:如果你希望在不阻塞当前线程的情况下执行某个操作,使用 BeginInvokeEndInvoke
  • 动态调用:如果你需要在运行时动态确定参数类型和数量,使用 DynamicInvoke,但要注意其性能开销。
  • 实时数据展示:用 Control.BeginInvoke 异步更新 UI。
  • 批量计算任务:通过 BeginInvoke 分发任务到线程池。
  • 插件系统:结合 DynamicInvoke 实现动态方法调用。

通过合理选择调用方式,可以在保证线程安全的同时提升程序性能与响应速度。

4. 联系与协作

  • 异步调用链
    BeginInvoke 启动异步任务 → 通过 IAsyncResult 监控状态 → EndInvoke 获取结果,形成完整的异步流程。
  • 线程安全
    UI 控件操作中,Control.InvokeControl.BeginInvoke 强制在 UI 线程执行委托,避免跨线程访问异常。
  • 替代方案
    现代 C# 推荐使用 Taskasync/await 替代 BeginInvoke/EndInvoke,因其代码可读性更高且资源管理更安全。

5. 注意事项

  1. 性能开销
    BeginInvoke 依赖线程池,频繁调用可能导致资源竞争;DynamicInvoke 的反射机制效率较低,慎用于高频场景。
  2. 异常处理
    • InvokeDynamicInvoke 的异常直接传播,需用 try-catch 包裹;
    • BeginInvoke 的异常需在 EndInvoke 中捕获。
  3. 代码优化
    发布模式下,编译器可能内联方法,可通过 [MethodImpl(MethodImplOptions.NoInlining)] 保留堆栈信息。

结语

回到目录页:C#/.NET 知识汇总
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。


网站公告

今日签到

点亮在社区的每一天
去签到