在现代异步编程里,CancellationTokenSource`是一个既强大又灵活的工具,能帮助我们管理并取消耗时较长的任务。接下来,我们会详细聊聊它的使用场景、一些推荐的做法,以及实际的例子。
CancellationTokenSource 是 .NET 中用于协作式取消操作的核心类。它允许您:
C#namespace AppCancellation
{
internal class Program
{
private static async Task<string> DownloadWebContentAsync(string url, CancellationToken cancellationToken)
{
using (var client = new HttpClient())
{
try
{
// 设置超时时间
client.Timeout = TimeSpan.FromSeconds(10);
// 发起异步GET请求,并传入取消令牌
var response = await client.GetAsync(url, cancellationToken);
// 确保请求成功
response.EnsureSuccessStatusCode();
// 读取响应内容
return await response.Content.ReadAsStringAsync(cancellationToken);
}
catch (OperationCanceledException)
{
// 捕获操作取消异常
Console.WriteLine("下载已被取消");
return string.Empty;
}
}
}
static async Task Main(string[] args)
{
// 创建取消令牌源
using (var cts = new CancellationTokenSource())
{
// 可以在另一个线程中取消操作
_ = Task.Run(() =>
{
// 例如3秒后取消
Thread.Sleep(3000);
cts.Cancel();
});
try
{
string result = await DownloadWebContentAsync("http://blog.idiosoft.com", cts.Token);
Console.WriteLine(result);
}
catch (Exception ex)
{
Console.WriteLine($"发生错误: {ex.Message}");
}
}
}
}
}

闭包(Closure)是一种强大的编程概念,它允许函数捕获并记住其定义时的环境上下文。在C#中,闭包使得函数可以访问并操作其词法作用域之外的变量。这个东西在JS中用的太多了。
闭包本质上是一个函数,它能够"记住"并访问其创建时的环境变量,即使这些变量已经超出了原始作用域。
C#public class ClosureDemo
{
public static Func<int> CreateCounter()
{
// count 是被捕获的局部变量
int count = 0;
// 返回一个匿名函数(闭包)
return () =>
{
count++; // 闭包可以修改外部变量
return count;
};
}
public static void Main()
{
// 创建两个独立的计数器
var counter1 = CreateCounter();
var counter2 = CreateCounter();
Console.WriteLine(counter1()); // 输出: 1
Console.WriteLine(counter1()); // 输出: 2
Console.WriteLine(counter2()); // 输出: 1 (独立的计数器)
}
}

在Windows应用程序中,播放系统声音是一个常见的需求。本文将详细介绍在C#中调用系统声音的多种方法,并提供具体的代码示例。
System.Media.SystemSounds 类提供了最简单的系统声音播放方式,包括常见的系统提示音。
C#using System.Media;
// 播放不同类型的系统声音
SystemSounds.Asterisk.Play(); // 信息提示音
SystemSounds.Beep.Play(); // 基本蜂鸣声
SystemSounds.Exclamation.Play();// 警告声
SystemSounds.Hand.Play(); // 错误提示音
SystemSounds.Question.Play(); // 询问声
C#using System.Media;
namespace AppSystemSound
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnPlayAsterisk_Click(object sender, EventArgs e)
{
SystemSounds.Asterisk.Play();
}
private void btnPlayBeep_Click(object sender, EventArgs e)
{
SystemSounds.Beep.Play();
}
private void btnPlayExclamation_Click(object sender, EventArgs e)
{
SystemSounds.Exclamation.Play();
}
}
}

延迟加载(Lazy Loading)是一种设计模式,它允许推迟对象的初始化,直到该对象真正被使用时才进行加载。这种技术可以帮助提高应用程序的性能和资源利用效率。
Lazy<T> 是.NET Framework 4.0引入的泛型类,提供了最简单的延迟加载实现。
C#using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AppLazy
{
public class UserProfile
{
// 延迟加载的复杂对象
// ThreadSafetyMode.ExecutionAndPublication 确保线程安全
private Lazy<ExpensiveResource> _expensiveResource;
public UserProfile()
{
// 使用 LazyThreadSafetyMode 确保线程安全
_expensiveResource = new Lazy<ExpensiveResource>(
() => new ExpensiveResource(),
LazyThreadSafetyMode.ExecutionAndPublication
);
}
public ExpensiveResource GetResource()
{
// 只有在调用Value时才真正创建对象
return _expensiveResource.Value;
}
}
public class ExpensiveResource
{
public Guid ResourceId { get; } = Guid.NewGuid();
public ExpensiveResource()
{
// 模拟复杂的初始化过程
Console.WriteLine($"正在初始化复杂资源 {ResourceId}...");
Thread.Sleep(2000); // 模拟耗时操作
Console.WriteLine($"复杂资源 {ResourceId} 初始化完成。");
}
}
}
C#namespace AppLazy
{
internal class Program
{
static void Main(string[] args)
{
// 创建 UserProfile 实例
UserProfile userProfile = new UserProfile();
Console.WriteLine("创建 UserProfile 实例,但尚未初始化资源...");
// 第一次访问 ExpensiveResource
Console.WriteLine("第一次获取资源:");
ExpensiveResource resource1 = userProfile.GetResource();
// 再次访问,不会重复初始化
Console.WriteLine("第二次获取资源:");
ExpensiveResource resource2 = userProfile.GetResource();
// 验证是否是同一个实例
Console.WriteLine($"两次获取的资源是否是同一个实例:{object.ReferenceEquals(resource1, resource2)}");
}
}
}

在 Windows 系统开发中,有时我们需要获取文件、文件夹或特定文件类型的系统图标。本文将详细介绍几种在 C# 中提取系统图标的方法。
在开始之前,需要引入以下命名空间:
C#using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
C#using System.Runtime.InteropServices;
namespace AppGetIcon
{
public partial class Form1 : Form
{
private const uint SHGFI_ICON = 0x000000100;
private const uint SHGFI_SMALLICON = 0x000000000;
private const uint SHGFI_LARGEICON = 0x000000000;
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SHGetFileInfo(
string pszPath,
uint dwFileAttributes,
ref SHFILEINFO psfi,
uint cbSizeFileInfo,
uint uFlags
);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct SHFILEINFO
{
public IntPtr hIcon;
public int iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
}
public Form1()
{
InitializeComponent();
}
public Icon GetFileIcon(string filePath, bool isSmallIcon = true)
{
SHFILEINFO shinfo = new SHFILEINFO();
uint flags = SHGFI_ICON | (isSmallIcon ? SHGFI_SMALLICON : SHGFI_LARGEICON);
IntPtr hImgSmall = SHGetFileInfo(
filePath,
0,
ref shinfo,
(uint)Marshal.SizeOf(shinfo),
flags
);
if (shinfo.hIcon == IntPtr.Zero)
return null;
return Icon.FromHandle(shinfo.hIcon);
}
private void btnGetIcon_Click(object sender, EventArgs e)
{
OpenFileDialog dialog = new OpenFileDialog();
if (dialog.ShowDialog() == DialogResult.OK)
{
string filePath = dialog.FileName;
Icon icon = GetFileIcon(filePath);
pictureBox1.Image = icon.ToBitmap();
}
}
}
}
