委托实战案例

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

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


1. 核心设计思路

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

2. 实战代码实现

csharp 复制代码
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 委托,你的下载器类可以被复用到任何项目中。
相关推荐
§ꦿCFོ༉3 小时前
嵌入式八股文—C语言入门学习(四)
c语言·开发语言·学习
投图匠3 小时前
十种不同风格的 CTP接口 框架
开发语言·python
爱笑的k116 小时前
nano-vllm 源码剖析:千行 Python 手写离线推理引擎的调度、显存与并行时序
开发语言·python·vllm
用户3721574261356 小时前
C# 提取 PDF 附件:包括文档附件和页面注释附件
c#
寂寞的船7 小时前
【Unity Bug挑战】第12期 Awaitable 二次 await 死锁之谜 —— 为什么同一个异步操作等两次就卡死?
unity·c#·游戏引擎·bug
X1A0RAN8 小时前
PPT 图片批量处理教程:导出全部幻灯片 · 提取全部素材
开发语言·javascript·powerpoint
熊猫钓鱼>_>9 小时前
普通用户视角的端侧智能体验手记【鸿蒙心迹】
开发语言·人工智能·华为·智能手机·性能优化·ai编程·harmonyos
瑞雪兆丰年兮9 小时前
JavaWeb-day13-SpringBootWeb事务管理&AOP
java·开发语言·springboot·javaweb·事务·web·aop
布莱克60511 小时前
Swift 常量详解:从 let 到编译期优化
服务器·开发语言·ssh·swift·常量·let
布莱克60511 小时前
Swift 可选类型(Optionals)详解:从原理到实战
开发语言·安全·ssh·swift·optional·可选类型