2025-10-13
C#
00

在现代异步编程里,CancellationTokenSource`是一个既强大又灵活的工具,能帮助我们管理并取消耗时较长的任务。接下来,我们会详细聊聊它的使用场景、一些推荐的做法,以及实际的例子。

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}"); } } } } }

image.png

2025-10-13
C#
00

什么是闭包?

闭包(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 (独立的计数器) } }

image.png

2025-10-13
C#
00

引言

在Windows应用程序中,播放系统声音是一个常见的需求。本文将详细介绍在C#中调用系统声音的多种方法,并提供具体的代码示例。

使用 System.Media.SystemSounds 类

基本使用方法

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(); } } }

image.png

2025-10-13
C#
00

什么是延迟加载?

延迟加载(Lazy Loading)是一种设计模式,它允许推迟对象的初始化,直到该对象真正被使用时才进行加载。这种技术可以帮助提高应用程序的性能和资源利用效率。

延迟加载的主要优点

  • 减少内存消耗
  • 提高应用程序启动速度
  • 按需加载资源
  • 降低系统开销

C#中的延迟加载实现方式

使用 Lazy 类

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)}"); } } }

image.png

2025-10-13
C#
00

在 Windows 系统开发中,有时我们需要获取文件、文件夹或特定文件类型的系统图标。本文将详细介绍几种在 C# 中提取系统图标的方法。

准备工作

在开始之前,需要引入以下命名空间:

C#
using System; using System.Drawing; using System.Runtime.InteropServices; using System.Windows.Forms;

方法一:使用 SHGetFileInfo 获取图标

图标提取方法

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(); } } } }

image.png