在实际开发中,我们经常需要处理耗时的异步操作,比如网络请求、文件读写等。有时候,我们可能需要取消这些正在进行的异步操作。本文将详细介绍如何在C#中实现异步操作的取消机制。
在开始之前,让我们了解几个重要的概念:
CancellationTokenSource: 用于发出取消信号的源CancellationToken: 用于接收取消信号的令牌Task: 表示异步操作的对象下面是一个完整的示例,展示如何实现可取消的异步操作:
C#using System.Diagnostics;
namespace AppCancellationToken
{
internal class Program
{
// 创建取消令牌源
static readonly CancellationTokenSource s_cts = new CancellationTokenSource();
// 创建HttpClient实例
static readonly HttpClient s_client = new HttpClient
{
MaxResponseContentBufferSize = 1_000_000
};
// 待下载的URL列表
static readonly IEnumerable<string> s_urlList = new string[]
{
"https://learn.microsoft.com",
"https://learn.microsoft.com/dotnet",
"https://learn.microsoft.com/azure",
"https://learn.microsoft.com/visualstudio"
};
static async Task Main()
{
Console.WriteLine("程序启动...");
Console.WriteLine("按回车键取消下载...\n");
// 创建监听取消的任务
Task cancelTask = Task.Run(() =>
{
while (Console.ReadKey().Key != ConsoleKey.Enter)
{
Console.WriteLine("按回车键取消下载...");
}
Console.WriteLine("\n检测到回车键:正在取消下载...\n");
s_cts.Cancel();
});
// 创建下载任务
Task sumPageSizesTask = SumPageSizesAsync();
// 等待任意一个任务完成
Task finishedTask = await Task.WhenAny(cancelTask, sumPageSizesTask);
if (finishedTask == cancelTask)
{
try
{
await sumPageSizesTask;
Console.WriteLine("在处理取消请求之前下载任务已完成。");
}
catch (OperationCanceledException)
{
Console.WriteLine("下载任务已被取消。");
}
}
Console.WriteLine("程序结束。");
}
static async Task SumPageSizesAsync()
{
var stopwatch = Stopwatch.StartNew();
int total = 0;
foreach (string url in s_urlList)
{
int contentLength = await ProcessUrlAsync(url, s_client, s_cts.Token);
total += contentLength;
}
stopwatch.Stop();
Console.WriteLine($"\n总计下载字节数: {total:#,#}");
Console.WriteLine($"耗时: {stopwatch.Elapsed}\n");
}
static async Task<int> ProcessUrlAsync(string url, HttpClient client, CancellationToken token)
{
HttpResponseMessage response = await client.GetAsync(url, token);
byte[] content = await response.Content.ReadAsByteArrayAsync(token);
Console.WriteLine($"{url,-60} {content.Length,10:#,#}");
return content.Length;
}
}
}
Microsoft.CSharp.RuntimeBinder 命名空间是 C# 动态语言运行时绑定的核心命名空间,主要用于支持动态编程特性。它包含了在运行时执行动态操作所需的类型,特别是在使用 dynamic 关键字时的底层实现。
这个命名空间主要用于:
这是动态操作失败时抛出的主要异常类型。
这是在动态绑定的内部编译过程中发生错误时抛出的异常。
C#using Microsoft.CSharp.RuntimeBinder;
namespace AppRuntimeBinder
{
internal class Program
{
static void Main(string[] args)
{
try
{
// 创建一个动态对象
dynamic dynamicObj = new System.Dynamic.ExpandoObject();
// 动态添加属性
dynamicObj.Name = "张三";
dynamicObj.Age = 25;
// 正常访问
Console.WriteLine($"姓名: {dynamicObj.Name}, 年龄: {dynamicObj.Age}");
// 尝试访问不存在的属性(会抛出异常)
Console.WriteLine(dynamicObj.Address);
}
catch (RuntimeBinderException ex)
{
// 捕获动态绑定异常
Console.WriteLine($"动态绑定错误: {ex.Message}");
}
}
}
}

SimpleWifi 是一个优秀的C#开发库,用于简化WiFi网络的管理操作。本文将详细介绍其使用方法和实际应用场景。
首先需要通过NuGet安装SimpleWifi包:
BashInstall-Package SimpleWifi.netstandard.Lsh

.NET Compiler Platform (Roslyn) SDK提供了强大的语法分析和转换功能。本文将介绍如何使用Roslyn进行代码转换,包括两种主要方法:
首先需要安装Visual Studio和.NET Compiler Platform SDK:

Roslyn 提供了两个主要的 API 来分析代码:语法 API(Syntax API)和语义 API(Semantic API)。其中,语法 API 用于分析代码的结构,而语义 API 则专注于分析代码的语义和含义。本文将重点介绍与语义分析相关的 API 使用方法。
Compilation代表编译器视角下的一个完整项目,包含:
C#// 创建编译单元
var compilation = CSharpCompilation.Create("HelloWorld")
.AddReferences(MetadataReference.CreateFromFile(
typeof(string).Assembly.Location))
.AddSyntaxTrees(syntaxTree);
SemanticModel提供了类似IDE智能提示的功能:
C#// 获取语义模型
SemanticModel model = compilation.GetSemanticModel(syntaxTree);
C#using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
namespace AppRoslyn
{
internal class Program
{
static void Main(string[] args)
{
// 示例程序
const string code = @"
using System;
namespace Demo
{
class Program
{
static void Main()
{
Console.WriteLine(""Hello"");
}
}
}";
// 解析语法树
SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
var root = tree.GetCompilationUnitRoot();
// 创建编译单元和语义模型
var compilation = CSharpCompilation.Create("Demo")
.AddReferences(MetadataReference.CreateFromFile(typeof(string).Assembly.Location))
.AddSyntaxTrees(tree);
var model = compilation.GetSemanticModel(tree);
// 获取using System的符号信息
var firstUsing = root.Usings[0];
var systemName = firstUsing.Name;
var symbolInfo = model.GetSymbolInfo(systemName);
// 分析System命名空间
var systemSymbol = (INamespaceSymbol)symbolInfo.Symbol;
foreach (var ns in systemSymbol.GetNamespaceMembers())
{
Console.WriteLine(ns); // 输出子命名空间
}
Console.ReadKey();
}
}
}
