委托实战案例

这个实战案例非常经典,它完美地结合了 Action<T>(进度回调委托)Task(异步操作载体)CancellationTokenSource(超时与取消机制)

我们将构建一个模拟下载器,它可以一边下载一边通过委托汇报进度,同时如果超过规定时间则自动停止。


1. 核心设计思路

  • 进度汇报: 使用 Action<int> 委托,由下载逻辑在循环中调用。
  • 超时处理: 使用 CancellationTokenSource 配合 CancelAfter 方法,这是现代 C# 处理超时的标准做法。
  • 异步封装: 使用 Task.Run 将下载逻辑放入后台,避免阻塞 UI。

2. 实战代码实现

using System;
using System.Threading;
using System.Threading.Tasks;

public class FileDownloader
{
    // 异步下载方法
    // progressCallback: 一个接收 int (0-100) 的委托,用于更新进度
    // ct: 用于监听超时或取消操作的令牌
    public async Task DownloadFileAsync(string url, Action<int> progressCallback, CancellationToken ct)
    {
        Console.WriteLine($"开始从 {url} 下载...");

        for (int i = 1; i <= 10; i++)
        {
            // 检查是否已经超时或被取消
            ct.ThrowIfCancellationRequested();

            // 模拟耗时的网络操作
            await Task.Delay(500, ct); 

            // 计算进度
            int progress = i * 10;

            // --- 委托的核心应用 ---
            // 调用外部传入的逻辑,调用者决定怎么显示进度(打印、进度条等)
            progressCallback?.Invoke(progress);
        }

        Console.WriteLine("\n下载完成!");
    }
}

class Program
{
    static async Task Main()
    {
        FileDownloader downloader = new FileDownloader();

        // 1. 设置超时时间为 3 秒 (若下载需要 5 秒则会触发超时)
        using var cts = new CancellationTokenSource();
        cts.CancelAfter(TimeSpan.FromSeconds(3)); 

        try
        {
            // 2. 定义进度回调委托 (使用 Lambda 表达式实现 Action<int>)
            Action<int> onProgress = (p) => {
                Console.Write($"\r当前进度: {p}% ");
            };

            // 3. 启动异步任务
            await downloader.DownloadFileAsync("https://example.com/bigfile.zip", onProgress, cts.Token);
        }
        catch (OperationCanceledException)
        {
            // 如果超时,会抛出此异常
            Console.WriteLine("\n[错误]: 下载超时或已被用户取消。");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"\n发生未知错误: {ex.Message}");
        }
    }
}


3. 深度解析:委托与任务的协作

1. 委托作为“解耦器”

在上面的代码中,FileDownloader 类并不知道如何“显示”进度。它只是持有一个 Action<int> 委托。

  • 如果是控制台程序,委托里写的是 Console.WriteLine
  • 如果是桌面 UI 程序,委托里写的可能是更新 ProgressBar 控件。
    这就实现了业务逻辑(下载)与展示逻辑(进度条)的完美分离

2. CancellationToken 的本质

CancellationToken 内部其实也依赖于委托。当你调用 CancelAfter 时,它会触发一个内部委托,通知所有持有该 Token 的代码进行“紧急避险”。

3. 执行流对比

环节 角色 作用
启动阶段 Task 开启异步执行上下文,不阻塞主线程。
运行阶段 Action<int> 作为双向通信的桥梁,从后台向前端发送实时状态。
终止阶段 CancellationToken 充当安全阀,由委托机制触发中断逻辑。

4. 为什么这样写?

  1. 非阻塞: 使用 await 确保了即使在下载,你的 UI 界面也不会卡死。
  2. 资源可控: 很多人写下载不考虑超时,导致后台线程死锁,使用 CancellationToken 是处理异步长任务的最佳实践。
  3. 高度抽象: 通过 Action 委托,你的下载器类可以被复用到任何项目中。
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容