二.c# 进阶

2026-05-12 08:01 372 阅读

一.高级语言特性

1.泛型

1.1 泛型基础概念

1). 什么是泛型?

泛型是C#和.NET框架中的一种强大特性,它允许您编写可以​​与任何数据类型一起工作​​的类、接口和方法,而无需提前指定具体的数据类型。 2).为什么需要泛型?

1.类型安全​​:编译时类型检查 2.​​代码重用​​:一套代码处理多种数据类型 3.​​性能优势​​:避免装箱拆箱(值类型) 4.减少强制转换​​:代码更简洁 3).泛型与非泛型对比

非泛型集合(ArrayList的问题)

ArrayList list = new ArrayList();
list.Add(1);       // 装箱(值类型→object)
list.Add("text");   // 任何类型都可以添加

int num = (int)list[0];  // 需要强制转换(拆箱)
// string str = (string)list[0];  // 运行时错误!

泛型集合(List的优势)

List<int> intList = new List<int>();
intList.Add(1);     // 无需装箱
// intList.Add("text");  // 编译时错误!

int num = intList[0];  // 无需强制转换



1.2 泛型类型定义与使用

1.2.1 泛型类

public class GenericClass<T>
{
    private T _value;
    
    public GenericClass(T value)
    {
        _value = value;
    }
    
    public T GetValue() => _value;
    
    public void ResetValue(T newValue)
    {
        _value = newValue;
    }
}

// 使用示例
var intContainer = new GenericClass<int>(42);
Console.WriteLine(intContainer.GetValue());  // 42

var stringContainer = new GenericClass<string>("Hello");
Console.WriteLine(stringContainer.GetValue());  // Hello

1.2.2 多类型参数

public class KeyValuePair<TKey, TValue>
{
    public TKey Key { get; set; }
    public TValue Value { get; set; }
    
    public KeyValuePair(TKey key, TValue value)
    {
        Key = key;
        Value = value;
    }
}

// 使用示例
var pair1 = new KeyValuePair<int, string>(1, "One");
var pair2 = new KeyValuePair<string, bool>("Enabled", true);

1.2.3 泛型接口

public interface IRepository<T>
{
    void Add(T entity);
    T GetById(int id);
    IEnumerable<T> GetAll();
}

public class ProductRepository : IRepository<Product>
{
    public void Add(Product entity) { /* 实现 */ }
    public Product GetById(int id) { /* 实现 */ return new Product(); }
    public IEnumerable<Product> GetAll() { /* 实现 */ yield break; }
}

1.2.4 泛型方法

public class Utility
{
    // 泛型方法
    public static T Max<T>(T a, T b) where T : IComparable<T>
    {
        return a.CompareTo(b) > 0 ? a : b;
    }
    
    // 非泛型类中的泛型方法
    public void DisplayType<T>(T obj)
    {
        Console.WriteLine($"Type: {typeof(T).Name}");
    }
}

// 使用示例
int maxInt = Utility.Max(3, 5);  // 类型推断
double maxDouble = Utility.Max(3.14, 2.71);

var util = new Utility();
util.DisplayType("Hello");  // Type: String
util.DisplayType(42);       // Type: Int32

1.3 泛型约束

(where子句) 1). 主要约束类型

约束类型 语法 说明
类约束 where T : class T必须是引用类型
结构约束 where T : struct T必须是值类型
基类约束 where T : BaseClass T必须继承自BaseClass
接口约束 where T : ISomeInterface T必须实现ISomeInterface
无参构造函数 where T : new() T必须有公共无参构造函数
裸类型约束 where T : U T必须继承自或实现U

2).约束示例

public class GenericWithConstraints<T> where T : class, IComparable, new()
{
    public T CreateAndCompare(T other)
    {
        T instance = new T();  // 需要new()约束
        return instance.CompareTo(other) > 0 ? instance : other;
    }
}

public struct Point : IComparable<Point>
{
    public int X, Y;
    
    public int CompareTo(Point other)
    {
        return (X + Y).CompareTo(other.X + other.Y);
    }
}

public class GenericStruct<T> where T : struct, IComparable<T>
{
    public T Max(T a, T b)
    {
        return a.CompareTo(b) > 0 ? a : b;
    }
}

// 使用示例
var pointComparer = new GenericStruct<Point>();
var maxPoint = pointComparer.Max(new Point { X=1, Y=2 }, new Point { X=3, Y=1 });

3).多约束组合

public class MultiConstraint<T> where T : class, IDisposable, new()
{
    public void UseAndDispose()
    {
        using (T obj = new T())
        {
            // 使用对象
        }
    }
}

1.4 泛型高级特性

1.4.1 协变(out)和逆变(in)

协变接口示例

public interface ICovariant<out T>
{
    T GetItem();
}

public class Animal { }
public class Dog : Animal { }

public class AnimalShelter : ICovariant<Animal>
{
    public Animal GetItem() => new Dog();  // Dog是Animal的子类
}

// 使用协变
ICovariant<Animal> shelter = new AnimalShelter();
Animal animal = shelter.GetItem();

逆变接口示例

public interface IContravariant<in T>
{
    void Process(T item);
}

public class AnimalProcessor : IContravariant<Animal>
{
    public void Process(Animal animal) { }
}

// 使用逆变
IContravariant<Dog> processor = new AnimalProcessor();
processor.Process(new Dog());

1.4.2 默认值表达式

public class DefaultValueExample<T>
{
    public T GetDefault()
    {
        return default(T);  // 引用类型返回null,值类型返回0/false等
    }
}

// 使用示例
var intDefault = new DefaultValueExample<int>().GetDefault();  // 0
var strDefault = new DefaultValueExample<string>().GetDefault();  // null

1.4.3 泛型与反射

Type openType = typeof(List<>);
Type closedType = openType.MakeGenericType(typeof(int));
object list = Activator.CreateInstance(closedType);

// 调用方法
MethodInfo addMethod = closedType.GetMethod("Add");
addMethod.Invoke(list, new object[] { 42 });

Console.WriteLine(list);  // 输出: System.Collections.Generic.List`1[System.Int32]

1.5 .NET内置泛型类型

1.5.1 集合类

类型 描述
List 动态数组
Dictionary<TKey,TValue> 键值对集合
Queue 先进先出队列
Stack 后进先出栈
HashSet 不重复元素集合
LinkedList 双向链表

1.5.2 接口

接口 描述
IEnumerable 可枚举集合
ICollection 集合基本操作
IList 可索引集合
IDictionary<TKey,TValue> 键值对集合
IComparable 可比较对象
IEquatable 可判断相等

1.5.3 委托

委托 描述
Action 无返回值方法
Func 有返回值方法
Predicate 返回bool的方法
Comparison 比较两个对象

1.6 拓展

1).命名约定

  • 单类型参数:T
  • 多类型参数:TKey, TValue, TResult等描述性名称

2).约束最小化

  • 只添加必要的约束
  • 过多的约束会限制泛型的灵活性

3).避免过度泛化​​

  • 不是所有类都需要成为泛型
  • 当确实需要处理多种类型时才使用泛型

4).​​性能考虑

  • 泛型在运行时生成特定类型的代码,避免装箱拆箱
  • 对于值类型,JIT会为每种值类型生成特定代码

5). 泛型在运行时如何工作? .NET运行时为每个不同的值类型参数创建特定的实现,而引用类型共享同一实现。

6).可以创建泛型枚举吗? 不可以,C#不支持泛型枚举。

7).泛型静态字段如何工作?

每个封闭类型(如List, List)都有自己的静态字段副本。 8). 如何检查T是否为特定类型?

if (typeof(T) == typeof(string))
{
    // 处理字符串特殊情况
}

9).泛型与dynamic有何区别? 泛型是编译时类型安全,dynamic是运行时绑定,会失去编译时类型检查。

2.LINQ

2.1 LINQ 概述

LINQ (Language Integrated Query) 是 .NET 框架中的一组技术,它提供了统一的查询语法来查询各种数据源。

2.1.1 LINQ 的主要组件

  • LINQ to Objects​​: 用于查询内存中的集合(例如List集合的数据)
  • LINQ to SQL​​: 用于查询关系数据库(逐渐遗弃)
  • ​​LINQ to XML​​: 用于查询 XML 文档
  • ​​LINQ to Entities​​: 用于查询 Entity Framework 数据模型

2.1.2 LINQ 的优势

  • 统一的查询语法
  • 编译时类型检查
  • IntelliSense 支持
  • 减少代码量,提高可读性

2.2 LINQ 查询语法

LINQ 提供了两种查询语法:查询表达式和方法语法。

2.2.1 查询表达式语法

var query = from item in collection
            where condition
            select item;

2.2.2 方法语法

var query = collection.Where(item => condition).Select(item => item);

2.3 基本 LINQ 操作

2.3.1 筛选数据 (Where)

// 查询表达式
var result = from num in numbers
             where num > 5
             select num;

// 方法语法
var result = numbers.Where(num => num > 5);

2.3.2 排序数据 (OrderBy, ThenBy)

// 升序排序
var sorted = numbers.OrderBy(n => n);

// 降序排序
var sortedDesc = numbers.OrderByDescending(n => n);

// 多级排序
var multiSorted = people.OrderBy(p => p.LastName).ThenBy(p => p.FirstName);

2.3.3 投影数据 (Select)

// 选择特定属性
var names = people.Select(p => p.Name);

// 创建匿名类型
var personInfos = people.Select(p => new { p.Name, p.Age });

2.3.4 分组数据 (GroupBy)

// 按年龄分组
var ageGroups = from p in people
                group p by p.Age into ageGroup
                select new { Age = ageGroup.Key, People = ageGroup };

// 方法语法
var ageGroups = people.GroupBy(p => p.Age)
                      .Select(g => new { Age = g.Key, People = g });

2.3.5 连接数据 (Join)

// 内连接
var joinedData = from p in people
                 join d in departments on p.DepartmentId equals d.Id
                 select new { p.Name, DepartmentName = d.Name };

// 方法语法
var joinedData = people.Join(departments,
                            p => p.DepartmentId,
                            d => d.Id,
                            (p, d) => new { p.Name, DepartmentName = d.Name });

2.4 聚合操作

2.4.1 常用聚合函数

// 计数
int count = numbers.Count();
int evenCount = numbers.Count(n => n % 2 == 0);

// 求和
int sum = numbers.Sum();

// 平均值
double avg = numbers.Average();

// 最大值/最小值
int max = numbers.Max();
int min = numbers.Min();

2.4.2 Aggregate

// 计算乘积
int product = numbers.Aggregate(1, (acc, num) => acc * num);

// 连接字符串
string concatenated = words.Aggregate((current, next) => current + " " + next);

2.5 元素操作

2.5.1 获取单个元素

// 第一个元素
var first = numbers.First();
var firstEven = numbers.First(n => n % 2 == 0);

// 最后一个元素
var last = numbers.Last();

// 单个元素(当只有一个元素时)
var single = numbers.Single(n => n == 5);

2.5.2 默认值处理

// 如果没有元素,返回默认值
var firstOrDefault = numbers.FirstOrDefault();
var singleOrDefault = numbers.SingleOrDefault(n => n == 5);

2.6 集合操作

2.6.1 去重 (Distinct)

var uniqueNumbers = numbers.Distinct();

2.6.2 并集 (Union)

var allNumbers = numbers1.Union(numbers2);

2.6.3 交集 (Intersect)

var commonNumbers = numbers1.Intersect(numbers2);

2.6.4 差集 (Except)

var numbersOnlyInFirst = numbers1.Except(numbers2);

2.7 分区操作

2.7.1 跳过元素 (Skip)

var allButFirstThree = numbers.Skip(3);

2.7.2 获取前几个元素 (Take)

var firstFive = numbers.Take(5);

2.7.3 分页实现

int pageSize = 10;
int pageNumber = 2;

var page = items.Skip((pageNumber - 1) * pageSize).Take(pageSize);

2.8 转换操作

2.8.1 转换为列表或数组

List<int> numberList = numbers.ToList();
int[] numberArray = numbers.ToArray();

2.8.2 转换为字典

Dictionary<int, Person> personDict = people.ToDictionary(p => p.Id);

2.8.3 转换为 Lookup (类似多值字典)

ILookup<int, Person> peopleByAge = people.ToLookup(p => p.Age);

2.9 量词操作

2.9.1 检查所有元素 (All)

bool allPositive = numbers.All(n => n > 0);

2.9.2 检查任意元素 (Any)

bool hasNegative = numbers.Any(n => n < 0);

2.9.3 包含特定元素 (Contains)

bool hasFive = numbers.Contains(5);

2.10 延迟执行与立即执行

2.10.1 延迟执行查询

var query = numbers.Where(n => n > 5); // 查询未执行
foreach (var num in query) // 查询在此处执行
{
    Console.WriteLine(num);
}

2.10.2 立即执行查询

var resultList = numbers.Where(n => n > 5).ToList(); // 查询立即执行
var count = numbers.Count(); // 查询立即执行

2.11 高级 LINQ

2.11.1 自定义扩展方法

public static IEnumerable<T> WhereNot<T>(this IEnumerable<T> collection, Func<T, bool> predicate)
{
    return collection.Where(item => !predicate(item));
}

// 使用
var nonEvens = numbers.WhereNot(n => n % 2 == 0);

2.11.2 使用 let 子句创建临时变量

var query = from p in people
            let fullName = p.FirstName + " " + p.LastName
            where fullName.Length > 10
            select fullName;

2.11.3 动态 LINQ

// 使用 System.Linq.Dynamic.Core 库
var query = people.AsQueryable().Where("Age > 30 && Name.StartsWith(\"J\")");

2.12 LINQ to XML

虽然复杂,但是简单。虽然简单,但是不常用,后续补充.....

2.13 LINQ to SQL

微软官方推荐学习 LINQ to Entities

2.14 LINQ to Entities

Entity Framework 的核心查询技术,将 LINQ 转换为优化的 SQL(支持多种数据库)。语法与内存操作类似,后续深入学习EF再研究。

// 1. 定义 DbContext(Code First)
public class AppDbContext : DbContext {
    public DbSet<Product> Products { get; set; }
    public DbSet<Category> Categories { get; set; }
}

// 2. 执行查询
using (var db = new AppDbContext()) {
    var expensiveProducts = from p in db.Products
                           where p.Price > 100
                           orderby p.Name
                           select p;

    // 包含关联数据(自动生成 JOIN)
    var productsWithCategory = db.Products
        .Include(p => p.Category)
        .ToList();
}

3.异步编程

3.1 概念

3.1.1 异步编程模型

  • 基于 Task 和 Task 的现代异步模式
  • 关键字:async/await
  • 设计目的:提高I/O密集型操作的吞吐量,避免线程阻塞

3.1.2 同步 vs 异步

特性 同步 异步
线程行为 阻塞当前线程 释放当前线程
适用场景 简单逻辑 I/O密集型或高延迟操作
资源利用 低效 高效

3.2 任务创建方式

3.2.1 基本创建方法

// 1. 使用Task.Run (推荐)
await Task.Run(() => {
    // CPU密集型工作
});

// 2. 使用Task.Factory.StartNew (需要更多控制时)
await Task.Factory.StartNew(() => {
    // 长时间运行的工作
}, TaskCreationOptions.LongRunning);

// 3. 直接返回任务
async Task<int> GetValueAsync() {
    return await SomeAsyncOperation();
}

3.2.2 任务类型选择指南

场景 推荐方式 原因
短期CPU密集型 Task.Run 自动使用线程池
长时间运行 Task.Factory.StartNew + LongRunning 避免线程池耗尽
I/O密集型 原生async API 最高效

3.3 等待模式

3.3.1 基本等待

await SomeMethodAsync(); // 顺序执行
await SomeMethod2Async(); // 顺序执行

3.3.2 并行等待

var task1 = Operation1Async();
var task2 = Operation2Async();

await Task.WhenAll(task1, task2); // 等待所有完成
// 或
var firstFinished = await Task.WhenAny(task1, task2); // 等待任意一个完成

3.3.3 超时控制

var task = SomeLongOperationAsync();
var timeout = Task.Delay(3000);

var completed = await Task.WhenAny(task, timeout);
if (completed == timeout) throw new TimeoutException();

3.4 取消机制

3.4.1 基本用法

var cts = new CancellationTokenSource();

// 设置超时自动取消
cts.CancelAfter(5000); 

try {
    await SomeAsyncMethod(cts.Token);
}
catch (OperationCanceledException) {
    // 处理取消
}

3.4.2 在方法中响应取消

async Task LongOperationAsync(CancellationToken ct) {
    ct.ThrowIfCancellationRequested();
    
    while(true) {
        ct.ThrowIfCancellationRequested();
        await Task.Delay(1000, ct);
    }
}

3.5 异常处理

3.5.1 基本模式

try {
    await SomeAsyncMethod();
}
catch (SpecificException ex) {
    // 处理特定异常
}
3.5.2 处理多个任务异常
try {
    await Task.WhenAll(task1, task2);
}
catch (AggregateException ae) {
    foreach (var e in ae.InnerExceptions) {
        // 处理每个异常
    }
}

3.6 提示

3.6.1 必须避免的做法

// 1. 混合阻塞和异步
var result = SomeAsyncMethod().Result;

// 2. 忽略任务
SomeAsyncMethod(); // 没有await

// 3. async void (除事件处理外)
async void BadMethod() { ... }

3.6.2 推荐实践

1.始终使用 async/await 贯穿整个调用链 2.异步方法名以"Async"结尾 3.在UI事件处理中使用 async void 但要捕获所有异常 4.考虑使用 ConfigureAwait(false) 避免不必要的上下文切换

3.6.3 性能

1.每个 async 方法会产生一个状态机对象 2.频繁的异步调用会增加GC压力 3.对于热路径(hot path)考虑使用 ValueTask 减少分配 4.避免过度创建任务

3.7 高级

3.7.1 异步流 (C# 8+)

async IAsyncEnumerable<int> GetNumbersAsync() {
    for (int i = 0; i < 10; i++) {
        await Task.Delay(100);
        yield return i;
    }
}

// 使用
await foreach (var num in GetNumbersAsync()) {
    Console.WriteLine(num);
}

3.7.2 值任务 (ValueTask)

public async ValueTask<int> CachedCalculationAsync() {
    if (cacheValid) return cachedValue;
    return await ComputeValueAsync();
}


4.多线程编程

4.1 多线程基础概念

4.1.1 线程基本概念

  • 线程​​:操作系统能够进行运算调度的最小单位
  • ​​多线程​​:一个进程中同时运行多个线程

优点​​: 1.提高CPU利用率 2.提高程序响应性 3.简化复杂任务的处理 4.更好地利用多核处理器

4.1.2 C# 多线程发展历程

1.原始线程​​:Thread 类 2.​​线程池​​:ThreadPool 类 3.​​任务并行库:Task 类 4.​​异步编程模型​​:async/await

4.2 Thread 类

// 创建并启动线程
Thread thread = new Thread(new ThreadStart(DoWork));
thread.Start();

void DoWork()
{
    Console.WriteLine("线程执行中...");
}

// 带参数的线程
Thread paramThread = new Thread(new ParameterizedThreadStart(DoWorkWithParam));
paramThread.Start("参数");

void DoWorkWithParam(object param)
{
    Console.WriteLine($"接收参数: {param}");
}

4.3 ThreadPool 线程池

// 使用线程池执行任务
ThreadPool.QueueUserWorkItem(DoWork);
ThreadPool.QueueUserWorkItem(DoWorkWithParam, "参数");

// 获取线程池信息
ThreadPool.GetAvailableThreads(out int workerThreads, out int completionPortThreads);
Console.WriteLine($"可用工作线程: {workerThreads}, 可用I/O线程: {completionPortThreads}");

4.4 Parallel 类

// 并行循环
Parallel.For(0, 10, i => 
{
    Console.WriteLine($"并行执行: {i}");
});

// 并行处理集合
List<string> items = new List<string> { "A", "B", "C", "D" };
Parallel.ForEach(items, item => 
{
    Console.WriteLine($"处理: {item}");
});

4.5 线程同步与线程安全

4.5.1 lock 关键字

private static readonly object _lockObj = new object();
private static int _counter = 0;

void Increment()
{
    lock (_lockObj)
    {
        _counter++;
    }
}

4.5.2 Monitor(监视器)

Monitor 是 .NET 提供的最基础的线程同步机制,C# 的 lock 关键字实际上是 Monitor 的语法糖。

private static readonly object _monitorObj = new object();

void DoWork()
{
    Monitor.Enter(_monitorObj);
    try
    {
        // 临界区代码
    }
    finally
    {
        Monitor.Exit(_monitorObj);
    }
}

4.5.3 Mutex 互斥体

Mutex 是比 Monitor 更重量级的同步原语,可以跨进程使用。

private static Mutex _mutex = new Mutex();

void DoWork()
{
    _mutex.WaitOne();
    try
    {
        // 临界区代码
    }
    finally
    {
        _mutex.ReleaseMutex();
    }
}

4.5.4 Semaphore 信号量

Semaphore 是一种计数信号量,用于控制对一组资源的访问。

private static Semaphore _semaphore = new Semaphore(3, 3); // 允许3个线程同时访问

void DoWork()
{
    _semaphore.WaitOne();
    try
    {
        // 受保护的代码
    }
    finally
    {
        _semaphore.Release();
    }
}

4.5.5 其他同步机制

  • AutoResetEvent​​ 和 ​​ManualResetEvent​​
  • ReaderWriterLockSlim​​ (读写锁)
  • Barrier​​ (屏障)
  • ​​CountdownEvent​​ (倒计数事件)

5.反射

反射是.NET框架提供的一种强大机制,它允许程序在运行时检查、访问和操作类型信息,甚至动态创建对象、调用方法和访问字段。

5.1 基础

1).什么是反射

反射是指程序可以访问、检测和修改自身状态或行为的能力。在C#中,反射主要用来:

  • 获取类型信息
  • 动态创建对象
  • 动态调用方法
  • 访问和修改字段/属性
  • 分析程序集结构

2).反射的核心类

  • System.Type - 表示类型声明
  • System.Reflection.Assembly - 表示程序集
  • System.Reflection.MethodInfo - 表示方法
  • System.Reflection.FieldInfo - 表示字段
  • System.Reflection.PropertyInfo - 表示属性
  • System.Reflection.ConstructorInfo - 表示构造函数

5.2 获取类型信息

1).获取Type对象的几种方式

// 1. 使用typeof运算符
Type type1 = typeof(string);

// 2. 使用对象的GetType()方法
string str = "Hello";
Type type2 = str.GetType();

// 3. 使用Type.GetType()静态方法
Type type3 = Type.GetType("System.String");

// 4. 从程序集获取类型
Assembly assembly = typeof(Program).Assembly;
Type type4 = assembly.GetType("Namespace.ClassName");

2).检查类型信息

Type type = typeof(StringBuilder);

// 基本类型信息
Console.WriteLine($"类型名称: {type.Name}");
Console.WriteLine($"完全限定名: {type.FullName}");
Console.WriteLine($"命名空间: {type.Namespace}");
Console.WriteLine($"是否是类: {type.IsClass}");
Console.WriteLine($"是否是值类型: {type.IsValueType}");

// 继承关系
Console.WriteLine($"基类: {type.BaseType}");
Console.WriteLine($"实现的接口: {string.Join(", ", type.GetInterfaces().Select(i => i.Name))}");

5.3 动态创建对象

1).使用Activator.CreateInstance

// 创建无参构造的对象
object stringBuilder = Activator.CreateInstance(typeof(StringBuilder));

// 创建带参数构造的对象
object list = Activator.CreateInstance(typeof(List<string>), new object[] { 10 });

// 泛型类型创建
Type genericListType = typeof(List<>);
Type concreteListType = genericListType.MakeGenericType(typeof(int));
object intList = Activator.CreateInstance(concreteListType);



2).使用ConstructorInfo

Type type = typeof(StringBuilder);
ConstructorInfo ctor = type.GetConstructor(new Type[] { typeof(string), typeof(int) });
object obj = ctor.Invoke(new object[] { "Hello", 100 });

5.4 动态调用方法

1).调用实例方法

// 创建StringBuilder实例
StringBuilder sb = new StringBuilder();

// 获取Append方法
MethodInfo appendMethod = typeof(StringBuilder).GetMethod("Append", new Type[] { typeof(string) });

// 动态调用
appendMethod.Invoke(sb, new object[] { "Hello, " });
appendMethod.Invoke(sb, new object[] { "Reflection!" });

Console.WriteLine(sb.ToString()); // 输出: Hello, Reflection!

2).调用静态方法

// 获取Console的WriteLine方法
MethodInfo writeLineMethod = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) });

// 调用静态方法
writeLineMethod.Invoke(null, new object[] { "Hello from Reflection!" });

3).调用泛型方法

// 获取List<T>的ConvertAll方法
Type listType = typeof(List<int>);
MethodInfo convertAllMethod = listType.GetMethod("ConvertAll");

// 创建具体的泛型方法
MethodInfo genericConvertAll = convertAllMethod.MakeGenericMethod(typeof(double));

// 准备参数
List<int> numbers = new List<int> { 1, 2, 3 };
Converter<int, double> converter = x => x * 1.5;

// 调用方法
var result = genericConvertAll.Invoke(numbers, new object[] { converter });
Console.WriteLine(string.Join(", ", (List<double>)result)); // 输出: 1.5, 3, 4.5

5.5 访问和修改字段/属性

1).访问和修改字段

public class Person
{
    private string name = "Unknown";
    public int Age { get; set; }
}

// 创建实例
Person person = new Person();

// 获取并设置私有字段
FieldInfo nameField = typeof(Person).GetField("name", BindingFlags.NonPublic | BindingFlags.Instance);
nameField.SetValue(person, "John Doe");

// 获取字段值
string nameValue = (string)nameField.GetValue(person);
Console.WriteLine(nameValue); // 输出: John Doe

2).访问和修改属性

// 获取Age属性
PropertyInfo ageProperty = typeof(Person).GetProperty("Age");

// 设置属性值
ageProperty.SetValue(person, 30);

// 获取属性值
int ageValue = (int)ageProperty.GetValue(person);
Console.WriteLine(ageValue); // 输出: 30

5.6 程序集操作

1).加载程序集

// 加载程序集
Assembly assembly = Assembly.LoadFrom("MyLibrary.dll");

// 或者从当前域加载
Assembly currentAssembly = Assembly.GetExecutingAssembly();

2).获取程序集中的类型

// 获取所有公共类型
Type[] publicTypes = assembly.GetExportedTypes();

// 获取所有类型(包括非公共的)
Type[] allTypes = assembly.GetTypes();

foreach (Type type in allTypes)
{
    Console.WriteLine(type.FullName);
}

6.特性

6.1 特性的基本概念

特性(Attribute)是C#中一种强大的元数据机制,它允许你向程序集、类型、成员等代码元素添加声明性信息。 介绍:

继承自 System.Attribute 类的特殊类
为代码元素添加元数据
不影响代码本身的执行逻辑
可通过反射在运行时读取

使用语法:

[AttributeName(PositionalParameter1, PositionalParameter2, NamedParameter1 = Value1, ...)]
public class MyClass { ... }

目标: 1.程序集(assembly) 2.模块(module) 3.类(class) 4.结构(struct) 5.接口(interface) 6.枚举(enum) 7.委托(delegate) 8.方法(method) 9.属性(property) 10.字段(field) 11.事件(event) 12.参数(parameter) 13.返回值(return value) 14.泛型参数(generic parameter)

6.2 常用内置特性

[Obsolete] - 标记过时代码

// 基本用法
[Obsolete("该方法已过时,请使用NewMethod代替")]
public void OldMethod() { }

// 设置为错误级别
[Obsolete("该方法已废弃,不允许再使用", true)]
public void DeprecatedMethod() { }

[Serializable] - 序列化控制

[Serializable]
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    
    [NonSerialized] // 标记不序列化的字段
    private string secretInfo;
}

[Conditional] - 条件编译

[Conditional("DEBUG")]
public void LogDebug(string message)
{
    Console.WriteLine($"DEBUG: {message}");
}

// 只有在DEBUG模式下才会调用
LogDebug("调试信息");

[DllImport] - 调用非托管代码

[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int MessageBox(IntPtr hWnd, string text, string caption, uint type);

// 调用示例
MessageBox(IntPtr.Zero, "Hello World", "提示", 0);

[Flags] - 枚举位标志

[Flags]
public enum Permissions
{
    None = 0,
    Read = 1,
    Write = 2,
    Execute = 4,
    ReadWrite = Read | Write,
    All = Read | Write | Execute
}

// 使用示例
var userPermissions = Permissions.Read | Permissions.Write;

6.3 自定义特性

创建:

[AttributeUsage(
    AttributeTargets.Class | AttributeTargets.Method, // 应用目标
    AllowMultiple = true,  // 是否允许多次应用
    Inherited = false)]    // 是否可被继承
public class AuthorAttribute : Attribute
{
    public string Name { get; }
    public string Version { get; set; }
    
    public AuthorAttribute(string name)
    {
        Name = name;
        Version = "1.0";
    }
}

使用:

[Author("张三", Version = "2.0")]
[Author("李四")] // AllowMultiple=true允许应用多个
public class MyClass
{
    [Author("王五")]
    public void MyMethod() { }
}

读取:

var classAttributes = typeof(MyClass).GetCustomAttributes(typeof(AuthorAttribute), false);
foreach (AuthorAttribute attr in classAttributes)
{
    Console.WriteLine($"作者: {attr.Name}, 版本: {attr.Version}");
}

var method = typeof(MyClass).GetMethod("MyMethod");
var methodAttributes = method.GetCustomAttributes(typeof(AuthorAttribute), false);

7.dynamic

dynamic 是C# 4.0引入的一个特殊类型,它提供了一种动态类型解析机制,允许在编译时绕过静态类型检查,将类型解析推迟到运行时。

7.1 基本概念

dynamic 是什么?

  • 静态类型声明,动态类型解析
  • 编译时不进行类型检查
  • 运行时通过DLR(Dynamic Language Runtime)解析
  • 可以表示任何对象 声明语法
dynamic variableName = value;

与 object 和 var 的区别

特性 dynamic object var
类型检查时间 运行时 编译时 编译时
需要转换 不需要 需要 不需要
智能感知 有(object的方法) 有(实际类型的方法)
性能 较慢 中等 最快

7.2 基本用法

简单使用

dynamic name = "John Doe";
Console.WriteLine(name.ToUpper()); // 运行时解析ToUpper方法

name = 10; // 可以重新赋值为不同类型
Console.WriteLine(name + 5); // 输出15

方法调用

dynamic calculator = new Calculator();
int result = calculator.Add(5, 3); // 运行时检查Add方法是否存在

属性访问

dynamic person = new ExpandoObject();
person.Name = "Alice";
person.Age = 30;
Console.WriteLine($"{person.Name} is {person.Age} years old");

7.3 高级用法

与反射结合

object obj = GetSomeObject(); // 不知道具体类型
dynamic dynObj = obj;
try 
{
    string result = dynObj.SomeMethod(); // 比反射代码更简洁
}
catch (RuntimeBinderException ex)
{
    Console.WriteLine("方法调用失败: " + ex.Message);
}

动态对象

dynamic expando = new ExpandoObject();
expando.Name = "Bob";
expando.ShowInfo = (Action)(() => Console.WriteLine(expando.Name));

expando.ShowInfo(); // 输出"Bob"

动态集合

dynamic list = new List<dynamic>();
list.Add(10);
list.Add("Hello");
list.Add(DateTime.Now);

foreach (var item in list)
{
    Console.WriteLine(item);
}

7.4 动态方法调用

方法缺失处理

dynamic obj = new MyDynamicObject();
try
{
    obj.NonExistentMethod();
}
catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException ex)
{
    Console.WriteLine("方法调用失败: " + ex.Message);
}

实现动态行为

public class DynamicSample : DynamicObject
{
    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        Console.WriteLine($"调用了方法: {binder.Name}");
        result = null;
        return true;
    }
}

// 使用
dynamic sample = new DynamicSample();
sample.AnyMethodName(); // 输出"调用了方法: AnyMethodName"

7.5 性能考虑

dynamic value = GetValue();
if (value is int intValue) // 模式匹配
{
    // 使用静态类型intValue操作,性能更好
}

7.6 实际应用

JSON处理

string json = "{\"name\":\"John\", \"age\":30}";
dynamic data = JsonConvert.DeserializeObject(json);
Console.WriteLine(data.name); // "John"
 动态查询

dynamic queryResult = ExecuteDynamicQuery("SELECT * FROM Users");
foreach (dynamic user in queryResult)
{
    Console.WriteLine(user.UserName);
}

插件系统

dynamic plugin = LoadPlugin("SomePlugin.dll");
plugin.Initialize();
plugin.Execute(config);

二.常用.NET库

1.集合

1.1 集合框架的全面整理

1.1.1 基本集合接口

接口 描述
IEnumerable 提供枚举集合的能力
ICollection 扩展IEnumerable,添加计数和修改方法
IList 支持按索引访问的有序集合
IDictionary<TKey,TValue> 键值对集合
ISet 提供集合操作(并集、交集等)
IReadOnlyCollection 只读集合
IReadOnlyList 只读列表
IReadOnlyDictionary<TKey,TValue> 只读字典

1.1.2 非泛型集合

集合类型 描述 对应泛型版本
ArrayList 动态数组 List
Hashtable 键值对集合 Dictionary<TKey,TValue>
Queue 先进先出集合 Queue
Stack 后进先出集合 Stack
SortedList 按键排序的键值对 SortedList<TKey,TValue>
BitArray 位值集合 无直接对应

注意​​:非泛型集合已过时,建议使用泛型集合。

1.1.3 泛型集合

列表集合

集合类型 描述 时间复杂度 线程安全
List 动态数组 访问O(1), 插入/删除O(n)
LinkedList 双向链表 访问O(n), 插入/删除O(1)
HashSet 无序唯一值集合 查找/插入/删除平均O(1)
SortedSet 排序唯一值集合 查找/插入/删除O(log n)
ImmutableList 不可变列表 修改操作O(n)

队列和栈

集合类型 描述 线程安全
Queue 先进先出(FIFO)集合
Stack 后进先出(LIFO)集合
ConcurrentQueue 线程安全队列
ConcurrentStack 线程安全栈

字典集合

集合类型 描述 时间复杂度 线程安全
Dictionary<TKey,TValue> 哈希表实现的键值对 查找/插入/删除平均O(1)
SortedDictionary<TKey,TValue> 二叉搜索树实现的键值对 查找/插入/删除O(log n)
SortedList<TKey,TValue> 数组实现的排序键值对 查找O(log n), 插入/删除O(n)
ConcurrentDictionary<TKey,TValue> 线程安全字典 操作平均O(1)
ImmutableDictionary<TKey,TValue> 不可变字典 修改操作O(log n)

1.1.4 线程安全集合

集合类型 描述 底层实现
ConcurrentBag 无序对象集合 线程本地存储
ConcurrentQueue 线程安全FIFO队列 链表+原子操作
ConcurrentStack 线程安全LIFO栈 链表+原子操作
ConcurrentDictionary<TKey,TValue> 线程安全字典 分段哈希表
BlockingCollection 生产者-消费者模型集合 封装ConcurrentQueue/Stack

1.1.5 不可变集合

集合类型 描述 特点
ImmutableArray 不可变数组 高性能,零分配
ImmutableList 不可变列表 修改返回新实例
ImmutableDictionary<TKey,TValue> 不可变字典 哈希数组映射树
ImmutableHashSet 不可变哈希集合 修改返回新实例
ImmutableSortedSet 不可变排序集合 修改返回新实例
ImmutableQueue 不可变队列 持久化数据结构
ImmutableStack 不可变栈 持久化数据结构

1.1.6 特殊用途集合

集合类型 描述 命名空间
NameValueCollection 字符串键值集合 System.Collections.Specialized
HybridDictionary 根据大小自动选择实现 System.Collections.Specialized
OrderedDictionary 保持插入顺序的字典 System.Collections.Specialized
BitVector32 紧凑布尔值/整数存储 System.Collections.Specialized
ObservableCollection 可通知变化的集合 System.Collections.ObjectModel
ReadOnlyObservableCollection 只读可观察集合 System.Collections.ObjectModel
ReadOnlyCollection 只读集合包装器 System.Collections.ObjectModel

1.1.7 集合选择指南

1.需要索引访问​​ → List 2.​​频繁插入/删除​​ → LinkedList 3.​​需要唯一值​​ → HashSet 4.​​需要排序唯一值​​ → SortedSet 5.​​键值对存储​​ → Dictionary<TKey,TValue> 6.​​需要排序键值对​​ → SortedDictionary<TKey,TValue> 7.​​多线程环境​​ → Concurrent 集合或 Immutable 集合 8.​​需要线程安全队列​​ → ConcurrentQueue 9.​​需要线程安全栈​​ → ConcurrentStack 10.​​需要生产者-消费者模式​​ → BlockingCollection 11.​​需要不可变数据​​ → Immutable 集合系列 

1.2 集合实用案例

1.2.1 列表集合

List - 动态数组

// 创建并初始化列表
List<string> fruits = new List<string> { "Apple", "Banana", "Cherry" };

// 添加元素
fruits.Add("Durian");
fruits.Insert(1, "Blueberry"); // 在索引1处插入

// 访问元素
string first = fruits[0]; // "Apple"

// 遍历列表
foreach (var fruit in fruits)
{
    Console.WriteLine(fruit);
}

// 删除元素
fruits.Remove("Banana");
fruits.RemoveAt(0); // 删除第一个元素

// 查找元素
bool hasApple = fruits.Contains("Apple");
int cherryIndex = fruits.IndexOf("Cherry");
LinkedList - 双向链表

// 创建链表
LinkedList<int> numbers = new LinkedList<int>();

// 添加元素
numbers.AddLast(10); // 末尾添加
numbers.AddFirst(5); // 开头添加
var node = numbers.AddAfter(numbers.First, 7); // 在第一个节点后添加

// 遍历链表
foreach (int num in numbers)
{
    Console.WriteLine(num); // 输出: 5, 7, 10
}

// 删除节点
numbers.Remove(7);
numbers.RemoveFirst();

1.2.2 集合(Set)

HashSet - 无序唯一集合

HashSet<string> productCodes = new HashSet<string>();

// 添加元素(自动去重)
productCodes.Add("P001");
productCodes.Add("P002");
productCodes.Add("P001"); // 不会被重复添加

// 检查存在
bool exists = productCodes.Contains("P001"); // true

// 集合操作
var set1 = new HashSet<int> { 1, 2, 3 };
var set2 = new HashSet<int> { 2, 3, 4 };

set1.UnionWith(set2);       // 并集: 1,2,3,4
set1.IntersectWith(set2);   // 交集: 2,3
set1.ExceptWith(set2);      // 差集: 1
SortedSet - 排序集合

SortedSet<int> scores = new SortedSet<int> { 90, 85, 95, 80 };

// 自动排序
foreach (var score in scores)
{
    Console.WriteLine(score); // 输出: 80,85,90,95
}

// 范围查询
var range = scores.GetViewBetween(85, 95); // 85,90,95

1.2.3 字典(Dictionary)

Dictionary<TKey,TValue> - 哈希字典

Dictionary<string, decimal> productPrices = new Dictionary<string, decimal>
{
    ["Laptop"] = 999.99m,
    ["Mouse"] = 25.50m
};

// 添加/修改
productPrices["Keyboard"] = 45.00m;
productPrices["Laptop"] = 899.99m; // 修改

// 访问元素
decimal mousePrice = productPrices["Mouse"];

// 安全访问
if (productPrices.TryGetValue("Monitor", out decimal price))
{
    Console.WriteLine($"Monitor price: {price}");
}

// 遍历字典
foreach (var kvp in productPrices)
{
    Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}
SortedDictionary<TKey,TValue> - 排序字典

SortedDictionary<string, int> wordCounts = new SortedDictionary<string, int>
{
    ["apple"] = 5,
    ["banana"] = 3,
    ["cherry"] = 8
};

// 按键自动排序
foreach (var entry in wordCounts)
{
    Console.WriteLine($"{entry.Key}: {entry.Value}"); // apple, banana, cherry
}

1.2.4 队列和栈

Queue - 先进先出队列

Queue<string> printerQueue = new Queue<string>();

// 入队
printerQueue.Enqueue("Document1.pdf");
printerQueue.Enqueue("Document2.pdf");

// 出队
while (printerQueue.Count > 0)
{
    string doc = printerQueue.Dequeue();
    Console.WriteLine($"Printing: {doc}");
}

Stack - 后进先出栈

Stack<string> browserHistory = new Stack<string>();

// 压栈
browserHistory.Push("google.com");
browserHistory.Push("microsoft.com");
browserHistory.Push("github.com");

// 弹栈
string lastVisited = browserHistory.Pop(); // github.com
string previous = browserHistory.Peek(); // microsoft.com (不移除)

1.2.5 线程安全集合

ConcurrentBag - 线程安全无序集合

ConcurrentBag<int> concurrentNumbers = new ConcurrentBag<int>();

// 多线程添加
Parallel.For(0, 10, i => 
{
    concurrentNumbers.Add(i);
});

// 遍历(顺序不确定)
foreach (var num in concurrentNumbers)
{
    Console.WriteLine(num);
}
ConcurrentDictionary<TKey,TValue> - 线程安全字典

ConcurrentDictionary<string, int> inventory = new ConcurrentDictionary<string, int>();

// 线程安全添加/更新
Parallel.For(0, 5, i => 
{
    inventory.AddOrUpdate($"Item{i}", 1, (key, oldValue) => oldValue + 1);
});

// 安全访问
if (inventory.TryGetValue("Item1", out int count))
{
    Console.WriteLine($"Item1 count: {count}");
}

BlockingCollection - 生产者消费者模型

BlockingCollection<string> messageQueue = new BlockingCollection<string>(boundedCapacity: 10);

// 生产者线程
Task.Run(() =>
{
    for (int i = 0; i < 5; i++)
    {
        messageQueue.Add($"Message {i}");
        Thread.Sleep(100);
    }
    messageQueue.CompleteAdding();
});

// 消费者线程
Task.Run(() =>
{
    foreach (var msg in messageQueue.GetConsumingEnumerable())
    {
        Console.WriteLine($"Processing: {msg}");
    }
}).Wait();

1.2.6 不可变集合

ImmutableList - 不可变列表

var originalList = ImmutableList.Create<string>("A", "B", "C");

// 修改返回新实例
var modifiedList = originalList.Add("D").Remove("B");

Console.WriteLine("Original:");
originalList.ForEach(Console.WriteLine); // A,B,C

Console.WriteLine("Modified:");
modifiedList.ForEach(Console.WriteLine); // A,C,D
ImmutableDictionary<TKey,TValue> - 不可变字典

var originalDict = ImmutableDictionary.Create<string, int>()
    .Add("One", 1)
    .Add("Two", 2);

// 修改返回新实例
var updatedDict = originalDict.SetItem("One", 100).Remove("Two");

Console.WriteLine(originalDict["One"]); // 1
Console.WriteLine(updatedDict["One"]);  // 100

1.2.7 特殊集合

ObservableCollection - 可观察集合

ObservableCollection<string> names = new ObservableCollection<string> { "Alice", "Bob" };

// 订阅集合变更事件
names.CollectionChanged += (sender, e) =>
{
    Console.WriteLine($"Change: {e.Action}");
    if (e.NewItems != null)
    {
        foreach (var newItem in e.NewItems)
        {
            Console.WriteLine($"Added: {newItem}");
        }
    }
};

// 修改集合会触发事件
names.Add("Charlie");
names.Remove("Alice");

BitArray - 位数组

BitArray bits = new BitArray(8);

// 设置位
bits[0] = true;
bits[1] = true;
bits.Set(2, true); // 另一种设置方式

// 遍历位
for (int i = 0; i < bits.Count; i++)
{
    Console.WriteLine($"Bit {i}: {bits[i]}");
}

// 位运算
BitArray anotherBits = new BitArray(8);
var result = bits.And(anotherBits);

2.i/o流和文件操作

I/O流是C#中处理数据输入输出的核心机制,提供了统一的接口来操作各种数据源(文件、内存、网络等)。

2.1 流(Stream)基础

Stream抽象类 System.IO.Stream是所有流的抽象基类

public abstract class Stream : IDisposable
{
    public abstract bool CanRead { get; }
    public abstract bool CanWrite { get; }
    public abstract bool CanSeek { get; }
    public abstract long Length { get; }
    public abstract long Position { get; set; }
    
    public abstract void Flush();
    public abstract int Read(byte[] buffer, int offset, int count);
    public abstract void Write(byte[] buffer, int offset, int count);
    public abstract long Seek(long offset, SeekOrigin origin);
    public abstract void SetLength(long value);
    
    // 其他方法和实现...
}

主要流类型

流类型 描述 典型用途
FileStream 文件流 文件读写
MemoryStream 内存流 内存数据操作
NetworkStream 网络流 网络通信
BufferedStream 缓冲流 提高I/O性能
GZipStream 压缩流 数据压缩/解压
CryptoStream 加密流 数据加密/解密

2.2 文件流(FileStream)

创建FileStream

// 方式1:直接创建
using (FileStream fs = new FileStream("test.dat", 
    FileMode.OpenOrCreate, 
    FileAccess.ReadWrite))
{
    // 使用文件流
}

// 方式2:通过File类
using (FileStream fs = File.Open("test.dat", FileMode.Open))
{
    // 使用文件流
}

// 异步创建
using (FileStream fs = new FileStream("test.dat", 
    FileMode.OpenOrCreate, 
    FileAccess.ReadWrite, 
    FileShare.None, 
    bufferSize: 4096, 
    useAsync: true))
{
    // 异步操作
}

文件流操作示例

// 写入文件
byte[] data = Encoding.UTF8.GetBytes("Hello, FileStream!");
using (FileStream fs = new FileStream("example.txt", FileMode.Create))
{
    fs.Write(data, 0, data.Length);
    fs.Flush(); // 确保数据写入磁盘
}

// 读取文件
using (FileStream fs = new FileStream("example.txt", FileMode.Open))
{
    byte[] buffer = new byte[fs.Length];
    int bytesRead = fs.Read(buffer, 0, buffer.Length);
    string content = Encoding.UTF8.GetString(buffer, 0, bytesRead);
    Console.WriteLine(content); // 输出: Hello, FileStream!
}

随机访问文件

using (FileStream fs = new FileStream("random.dat", FileMode.Create))
{
    // 写入一些数据
    for (int i = 0; i < 10; i++)
    {
        fs.WriteByte((byte)i);
    }
    
    // 定位到第5个字节
    fs.Seek(4, SeekOrigin.Begin);
    
    // 读取并修改
    byte value = (byte)fs.ReadByte();
    fs.Seek(-1, SeekOrigin.Current);
    fs.WriteByte((byte)(value * 2));
    
    // 验证修改
    fs.Seek(4, SeekOrigin.Begin);
    Console.WriteLine(fs.ReadByte()); // 输出: 8 (4 * 2)
}

2.3 内存流(MemoryStream)

基本使用

// 创建并写入内存流
using (MemoryStream ms = new MemoryStream())
{
    byte[] data = Encoding.ASCII.GetBytes("MemoryStream Example");
    ms.Write(data, 0, data.Length);
    
    // 读取内存流
    ms.Position = 0; // 重置位置
    byte[] buffer = new byte[ms.Length];
    ms.Read(buffer, 0, buffer.Length);
    Console.WriteLine(Encoding.ASCII.GetString(buffer));
}

// 从字节数组创建
byte[] initialData = { 1, 2, 3, 4, 5 };
using (MemoryStream ms = new MemoryStream(initialData))
{
    // 读取并修改
    int b = ms.ReadByte();
    while (b != -1)
    {
        Console.Write(b + " "); // 输出: 1 2 3 4 5
        b = ms.ReadByte();
    }
}

高级用法

// 使用内存流作为缓冲区
using (MemoryStream ms = new MemoryStream())
{
    // 写入不同类型数据
    BinaryWriter writer = new BinaryWriter(ms);
    writer.Write(123);
    writer.Write(3.14);
    writer.Write("Hello");
    
    // 读取数据
    ms.Position = 0;
    BinaryReader reader = new BinaryReader(ms);
    int intValue = reader.ReadInt32();
    double doubleValue = reader.ReadDouble();
    string stringValue = reader.ReadString();
    
    Console.WriteLine($"{intValue}, {doubleValue}, {stringValue}");
}

2.4 缓冲流(BufferedStream)

// 使用缓冲流提高性能
using (FileStream fs = new FileStream("largefile.dat", FileMode.Open))
using (BufferedStream bs = new BufferedStream(fs, 8192)) // 8KB缓冲区
{
    byte[] buffer = new byte[1024];
    int bytesRead;
    
    while ((bytesRead = bs.Read(buffer, 0, buffer.Length)) > 0)
    {
        // 处理数据
    }
}

// 写入时使用缓冲
using (FileStream fs = new FileStream("output.dat", FileMode.Create))
using (BufferedStream bs = new BufferedStream(fs))
{
    for (int i = 0; i < 10000; i++)
    {
        bs.WriteByte((byte)(i % 256));
    }
}

2.5 流读写器

流读写器(StreamReader/StreamWriter) 文本读写

// 写入文本文件
using (FileStream fs = new FileStream("text.txt", FileMode.Create))
using (StreamWriter writer = new StreamWriter(fs, Encoding.UTF8))
{
    writer.WriteLine("第一行");
    writer.WriteLine("第二行");
    writer.Write("没有换行");
}

// 读取文本文件
using (FileStream fs = new FileStream("text.txt", FileMode.Open))
using (StreamReader reader = new StreamReader(fs, Encoding.UTF8))
{
    string line;
    while ((line = reader.ReadLine()) != null)
    {
        Console.WriteLine(line);
    }
    
    // 或者读取全部内容
    // string content = reader.ReadToEnd();
}

二进制读写(BinaryReader/BinaryWriter)

// 写入二进制数据
using (FileStream fs = new FileStream("data.bin", FileMode.Create))
using (BinaryWriter writer = new BinaryWriter(fs))
{
    writer.Write(42);            // int
    writer.Write(3.14159);       // double
    writer.Write(true);          // bool
    writer.Write("Hello");       // string
}

// 读取二进制数据
using (FileStream fs = new FileStream("data.bin", FileMode.Open))
using (BinaryReader reader = new BinaryReader(fs))
{
    int intValue = reader.ReadInt32();
    double doubleValue = reader.ReadDouble();
    bool boolValue = reader.ReadBoolean();
    string stringValue = reader.ReadString();
    
    Console.WriteLine($"{intValue}, {doubleValue}, {boolValue}, {stringValue}");
}

2.6 异步流操作

异步读写

// 异步写入
async Task WriteToFileAsync(string path, string content)
{
    byte[] data = Encoding.UTF8.GetBytes(content);
    
    using (FileStream fs = new FileStream(path, 
        FileMode.Create, 
        FileAccess.Write, 
        FileShare.None, 
        bufferSize: 4096, 
        useAsync: true))
    {
        await fs.WriteAsync(data, 0, data.Length);
    }
}

// 异步读取
async Task<string> ReadFromFileAsync(string path)
{
    using (FileStream fs = new FileStream(path, 
        FileMode.Open, 
        FileAccess.Read, 
        FileShare.Read, 
        bufferSize: 4096, 
        useAsync: true))
    {
        byte[] buffer = new byte[fs.Length];
        await fs.ReadAsync(buffer, 0, buffer.Length);
        return Encoding.UTF8.GetString(buffer);
    }
}

// 使用示例
await WriteToFileAsync("async.txt", "异步I/O示例");
string content = await ReadFromFileAsync("async.txt");
Console.WriteLine(content);

异步复制文件

async Task CopyFileAsync(string sourcePath, string destinationPath)
{
    using (FileStream sourceStream = new FileStream(sourcePath, 
        FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true))
    using (FileStream destinationStream = new FileStream(destinationPath, 
        FileMode.Create, FileAccess.Write, FileShare.None, 4096, true))
    {
        await sourceStream.CopyToAsync(destinationStream);
    }
}

2.7 特殊流类型

2.7.1 压缩流(GZipStream)

// 压缩数据
byte[] dataToCompress = Encoding.UTF8.GetBytes(new string('a', 1000));

using (MemoryStream ms = new MemoryStream())
{
    using (GZipStream gzip = new GZipStream(ms, CompressionMode.Compress))
    {
        gzip.Write(dataToCompress, 0, dataToCompress.Length);
    }
    
    byte[] compressedData = ms.ToArray();
    Console.WriteLine($"原始大小: {dataToCompress.Length}, 压缩后: {compressedData.Length}");
}

// 解压数据
byte[] compressedData = GetCompressedData(); // 获取压缩数据

using (MemoryStream ms = new MemoryStream(compressedData))
using (GZipStream gzip = new GZipStream(ms, CompressionMode.Decompress))
using (MemoryStream output = new MemoryStream())
{
    gzip.CopyTo(output);
    byte[] decompressedData = output.ToArray();
    Console.WriteLine(Encoding.UTF8.GetString(decompressedData));
}

2.7.1 加密流(CryptoStream)

// 使用AES加密
using (Aes aes = Aes.Create())
{
    byte[] key = aes.Key;
    byte[] iv = aes.IV;
    
    // 加密
    using (FileStream fs = new FileStream("encrypted.dat", FileMode.Create))
    using (CryptoStream cs = new CryptoStream(fs, 
        aes.CreateEncryptor(), 
        CryptoStreamMode.Write))
    {
        byte[] data = Encoding.UTF8.GetBytes("秘密数据");
        cs.Write(data, 0, data.Length);
    }
    
    // 解密
    using (FileStream fs = new FileStream("encrypted.dat", FileMode.Open))
    using (CryptoStream cs = new CryptoStream(fs, 
        aes.CreateDecryptor(key, iv), 
        CryptoStreamMode.Read))
    using (StreamReader reader = new StreamReader(cs))
    {
        string decrypted = reader.ReadToEnd();
        Console.WriteLine(decrypted); // 输出: 秘密数据
    }
}

2.8 文件系统

2.8.1 Directory

Directory 是一个静态类,提供了一系列静态方法用于目录操作,适合简单的单次目录操作。

基本目录操作


using System.IO;

// 创建目录
Directory.CreateDirectory(@"C:\MyApp\Data");

// 检查目录是否存在
bool exists = Directory.Exists(@"C:\MyApp");

// 删除目录(recursive参数决定是否删除非空目录)
Directory.Delete(@"C:\OldApp", recursive: true);

// 移动/重命名目录
Directory.Move(@"C:\MyApp\Temp", @"D:\Backup\MyAppTemp");
获取目录内容

// 获取目录下所有文件
string[] allFiles = Directory.GetFiles(@"C:\MyApp");

// 获取特定扩展名的文件(搜索所有子目录)
string[] txtFiles = Directory.GetFiles(@"C:\MyApp", "*.txt", SearchOption.AllDirectories);

// 获取所有子目录
string[] subDirectories = Directory.GetDirectories(@"C:\MyApp");

// 获取目录下所有文件系统条目(文件和目录)
string[] fileSystemEntries = Directory.GetFileSystemEntries(@"C:\MyApp");
目录属性操作

// 获取目录创建时间
DateTime createTime = Directory.GetCreationTime(@"C:\MyApp");

// 获取最后访问时间
DateTime accessTime = Directory.GetLastAccessTime(@"C:\MyApp");

// 获取最后写入时间
DateTime writeTime = Directory.GetLastWriteTime(@"C:\MyApp");

// 设置目录属性
Directory.SetCreationTime(@"C:\MyApp", DateTime.Now);
Directory.SetLastAccessTime(@"C:\MyApp", DateTime.Now);
Directory.SetLastWriteTime(@"C:\MyApp", DateTime.Now);

// 获取当前工作目录
string currentDir = Directory.GetCurrentDirectory();

// 设置当前工作目录
// 当应用程序终止时,工作目录将还原到其原始位置 (启动进程的目录) 。
Directory.SetCurrentDirectory(@"C:\MyApp");

2.8.2 DirectoryInfo

DirectoryInfo 是一个实例类,需要创建对象实例来使用,适合需要对同一目录进行多次操作的场景。

创建和使用 DirectoryInfo

// 创建DirectoryInfo实例
DirectoryInfo dirInfo = new DirectoryInfo(@"C:\MyApp");

// 检查目录是否存在
bool exists = dirInfo.Exists;

// 创建目录(如果不存在)
if (!dirInfo.Exists)
{
    dirInfo.Create();
}

// 创建子目录
DirectoryInfo subDir = dirInfo.CreateSubdirectory("Data");
获取目录内容

// 获取所有文件
FileInfo[] files = dirInfo.GetFiles();

// 获取特定扩展名的文件(搜索所有子目录)
FileInfo[] pdfFiles = dirInfo.GetFiles("*.pdf", SearchOption.AllDirectories);

// 获取所有子目录
DirectoryInfo[] subDirs = dirInfo.GetDirectories();

// 获取父目录
DirectoryInfo parentDir = dirInfo.Parent;
目录操作

// 移动目录
dirInfo.MoveTo(@"D:\Backup\MyApp");

// 删除目录,添加参数True表示递归删除
dirInfo.Delete(true);

// 刷新目录信息(当目录可能被外部修改时)
dirInfo.Refresh();
目录属性

// 获取/设置目录属性
DateTime createTime = dirInfo.CreationTime;
dirInfo.CreationTime = DateTime.Now;

DateTime accessTime = dirInfo.LastAccessTime;
dirInfo.LastAccessTime = DateTime.Now;

DateTime writeTime = dirInfo.LastWriteTime;
dirInfo.LastWriteTime = DateTime.Now;

// 获取目录名称和路径
string name = dirInfo.Name;          // "MyApp"
string fullName = dirInfo.FullName;   // "C:\MyApp"
string root = dirInfo.Root.FullName;  // "C:\"

2.8.3 DirectoryInfo拓展

public static class DirectoryInfoExtension
{
    /// <summary>
    /// 复制目录
    /// </summary>
    /// <param name="directoryInfo"></param>
    /// <param name="destDir"></param>
    public static void Copy(this DirectoryInfo directoryInfo, string destDir)
    {
        CopyDirectory(directoryInfo,destDir);
    }

    private static void CopyDirectory(DirectoryInfo source, string destDir)
    {
        var target = new DirectoryInfo(destDir);
        if (!source.Exists)
            throw new DirectoryNotFoundException("源目录不存在: " + source.FullName);

        if (!target.Exists)
            target.Create();

        // 复制所有文件
        foreach (FileInfo file in source.GetFiles())
        {
            file.CopyTo(Path.Combine(target.FullName, file.Name), true);
        }

        // 递归复制子目录
        foreach (DirectoryInfo subDir in source.GetDirectories())
        {
            CopyDirectory(subDir, Path.Combine(target.FullName, subDir.Name));
        }
    }

    /// <summary>
    /// 目录大小
    /// </summary>
    /// <param name="directoryInfo"></param>
    /// <returns></returns>
    public static long Size(this DirectoryInfo directoryInfo)
    {
        return CalculateDirectorySize(directoryInfo);
    }

    private static long CalculateDirectorySize(DirectoryInfo directory)
    {
        long size = 0;
        foreach (FileInfo file in directory.GetFiles())
        {
            size += file.Length;
        }
        foreach (DirectoryInfo subDir in directory.GetDirectories())
        {
            size += CalculateDirectorySize(subDir);
        }

        return size;
    }
}

2.8.4 File

在C#中,文件操作主要通过 System.IO 命名空间下的 File 和 FileInfo 类来实现。

基本文件操作

using System.IO;
using System.Text;

string filePath = @"C:\MyApp\data.txt";

// 创建文件并写入内容
File.WriteAllText(filePath, "Hello, World!");

// 追加内容到文件
File.AppendAllText(filePath, "\nThis is additional content.");

// 读取文件全部内容
string content = File.ReadAllText(filePath);

// 检查文件是否存在
bool exists = File.Exists(filePath);

// 删除文件
File.Delete(filePath);

// 复制文件
File.Copy(@"C:\source.txt", @"D:\backup.txt", overwrite: true);

// 移动/重命名文件
File.Move(@"C:\oldname.txt", @"C:\newname.txt");
按行读写文件

// 写入多行内容
string[] lines = { "First line", "Second line", "Third line" };
File.WriteAllLines(filePath, lines);

// 读取所有行
string[] readLines = File.ReadAllLines(filePath);

// 逐行读取(内存效率更高)
IEnumerable<string> linesEnumerable = File.ReadLines(filePath);
foreach (string line in linesEnumerable)
{
    Console.WriteLine(line);
}

二进制文件操作

// 写入二进制数据
byte[] data = { 0x48, 0x65, 0x6C, 0x6C, 0x6F }; // "Hello"的ASCII码
File.WriteAllBytes(filePath, data);

// 读取二进制数据
byte[] readData = File.ReadAllBytes(filePath);
文件属性操作

// 获取/设置文件属性
DateTime createTime = File.GetCreationTime(filePath);
File.SetCreationTime(filePath, DateTime.Now);

DateTime accessTime = File.GetLastAccessTime(filePath);
File.SetLastAccessTime(filePath, DateTime.Now);

DateTime writeTime = File.GetLastWriteTime(filePath);
File.SetLastWriteTime(filePath, DateTime.Now);

// 获取文件属性(只读、隐藏等)
FileAttributes attributes = File.GetAttributes(filePath);

// 设置文件属性
File.SetAttributes(filePath, FileAttributes.ReadOnly | FileAttributes.Hidden);

2.8.5 FileInfo

创建和使用 FileInfo

FileInfo fileInfo = new FileInfo(filePath);

// 检查文件是否存在
bool exists = fileInfo.Exists;

// 创建空文件
if (!fileInfo.Exists)
{
    using (fileInfo.Create()) { }
}

// 删除文件
fileInfo.Delete();

// 刷新文件信息(当文件可能被外部修改时)
fileInfo.Refresh();

文件操作

// 复制文件
fileInfo.CopyTo(@"D:\backup.txt", overwrite: true);

// 移动/重命名文件
fileInfo.MoveTo(@"C:\newname.txt");

// 打开文件流
using (FileStream stream = fileInfo.Open(FileMode.Open, FileAccess.ReadWrite))
{
    // 使用文件流进行操作
}

// 创建文本写入器
using (StreamWriter writer = fileInfo.CreateText())
{
    writer.WriteLine("Hello, FileInfo!");
}

// 打开文本读取器
using (StreamReader reader = fileInfo.OpenText())
{
    string content = reader.ReadToEnd();
}

文件属性

// 获取文件信息
string name = fileInfo.Name;          // "data.txt"
string fullName = fileInfo.FullName;   // "C:\MyApp\data.txt"
string extension = fileInfo.Extension; // ".txt"
string dirName = fileInfo.DirectoryName; // "C:\MyApp"
DirectoryInfo dir = fileInfo.Directory; // 获取父目录的DirectoryInfo对象

// 获取/设置文件时间
DateTime createTime = fileInfo.CreationTime;
fileInfo.CreationTime = DateTime.Now;

DateTime accessTime = fileInfo.LastAccessTime;
fileInfo.LastAccessTime = DateTime.Now;

DateTime writeTime = fileInfo.LastWriteTime;
fileInfo.LastWriteTime = DateTime.Now;

// 获取文件大小(字节)
long length = fileInfo.Length;

// 获取/设置文件属性
FileAttributes attributes = fileInfo.Attributes;
fileInfo.Attributes = FileAttributes.ReadOnly | FileAttributes.Archive;

3.日期和时间处理

日期和时间处理主要通过System.DateTime、System.TimeSpan和System.DateTimeOffset等类型来实现

3.1 DateTime 基础

// 获取当前时间
DateTime now = DateTime.Now;        // 本地时间
DateTime utcNow = DateTime.UtcNow;  // UTC时间

// 创建特定日期
DateTime date1 = new DateTime(2023, 5, 15); // 2023年5月15日
DateTime date2 = new DateTime(2023, 5, 15, 14, 30, 0); // 2023年5月15日14:30:00

// 解析字符串为DateTime
DateTime parsedDate = DateTime.Parse("2023-05-15");
DateTime parsedDateExact = DateTime.ParseExact("15/05/2023", "dd/MM/yyyy", null);

// 格式化日期为字符串
string formattedDate = now.ToString("yyyy-MM-dd HH:mm:ss");
string shortDate = now.ToShortDateString();
string longDate = now.ToLongDateString();

3.2 日期运算

// 加减时间
DateTime tomorrow = now.AddDays(1);
DateTime yesterday = now.AddDays(-1);
DateTime inOneHour = now.AddHours(1);

// 计算时间差
TimeSpan difference = date2 - date1;
double daysDifference = difference.TotalDays;
double hoursDifference = difference.TotalHours;

// 比较日期
bool isAfter = date2 > date1;
int compareResult = DateTime.Compare(date1, date2);

3.3 DateTime 属性

int year = now.Year;       // 年
int month = now.Month;     // 月
int day = now.Day;         // 日
int hour = now.Hour;       // 时
int minute = now.Minute;   // 分
int second = now.Second;   // 秒
DayOfWeek dayOfWeek = now.DayOfWeek; // 星期几

3.4 TimeSpan 使用

// 创建TimeSpan
TimeSpan timeSpan1 = new TimeSpan(1, 30, 0); // 1小时30分钟
TimeSpan timeSpan2 = TimeSpan.FromHours(2.5); // 2.5小时

// 运算
TimeSpan sum = timeSpan1 + timeSpan2;
TimeSpan difference = timeSpan1 - timeSpan2;

// 属性
double totalHours = timeSpan1.TotalHours;
int minutes = timeSpan1.Minutes;
DateTimeOffset 和时区处理

// DateTimeOffset包含时区信息
DateTimeOffset offsetNow = DateTimeOffset.Now;

// 时区转换
TimeZoneInfo localZone = TimeZoneInfo.Local;
TimeZoneInfo utcZone = TimeZoneInfo.Utc;
TimeZoneInfo otherZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");

DateTime convertedTime = TimeZoneInfo.ConvertTime(now, localZone, otherZone);

3.5 其他实用方法

// 获取月份天数
int daysInMonth = DateTime.DaysInMonth(2023, 2); // 2023年2月的天数

// 判断闰年
bool isLeapYear = DateTime.IsLeapYear(2024);

// 获取Unix时间戳
long unixTimestamp = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;

// 从Unix时间戳创建DateTime
DateTime dateFromUnix = DateTimeOffset.FromUnixTimeSeconds(unixTimestamp).DateTime;

4.正则表达式

正则表达式(Regular Expression)是处理字符串的强大工具

4.1 基本组件

4.1.1 正则表达式类

  • Regex:表示不可变的正则表达式
  • Match:表示单个正则表达式匹配的结果
  • MatchCollection:表示通过迭代方式将正则表达式模式应用于输入字符串所找到的成功匹配的集合
  • Group:表示单个捕获组的结果
  • GroupCollection:表示多个捕获组的集合

4.1.2 基本使用

using System.Text.RegularExpressions;

// 简单匹配
string pattern = @"\d+";  // 匹配一个或多个数字
string input = "abc123xyz456";
bool isMatch = Regex.IsMatch(input, pattern);  // 返回true

// 获取匹配
Match match = Regex.Match(input, pattern);
if (match.Success)
{
    Console.WriteLine(match.Value);  // 输出"123"
}

// 获取所有匹配
MatchCollection matches = Regex.Matches(input, pattern);
foreach (Match m in matches)
{
    Console.WriteLine(m.Value);  // 输出"123"和"456"
}

4.2 常用元字符

元字符 描述
. 匹配除换行符外的任意字符
\d 匹配数字(0-9)
\D 匹配非数字
\w 匹配单词字符(字母、数字、下划线)
\W 匹配非单词字符
\s 匹配空白字符(空格、制表符、换行符等)
\S 匹配非空白字符
^ 匹配字符串开头
$ 匹配字符串结尾
\b 匹配单词边界
\B 匹配非单词边界

4.3 量词

量词 描述

|* |匹配0次或多次| |+ |匹配1次或多次| |? |匹配0次或1次| |{n} |匹配恰好n次| |{n,} |匹配至少n次| |{n,m} |匹配n到m次|

4.4 字符类

// 匹配a、b或c中的任意一个字符
string pattern = @"[abc]";

// 匹配a到z的任意小写字母
string pattern = @"[a-z]";

// 匹配非数字字符
string pattern = @"[^0-9]";

4.5 分组

// 简单分组
string pattern = @"(abc)+";  // 匹配一个或多个"abc"

// 命名分组
string pattern = @"(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})";
Match m = Regex.Match("2023-05-15", pattern);
if (m.Success)
{
    Console.WriteLine(m.Groups["year"].Value);   // "2023"
    Console.WriteLine(m.Groups["month"].Value);  // "05"
    Console.WriteLine(m.Groups["day"].Value);    // "15"
}

4.6 常见正则表达式示例

电子邮件

string emailPattern = @"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$";
bool isValidEmail = Regex.IsMatch("user@example.com", emailPattern);

手机号码

string phonePattern = @"^1[3-9]\d{9}$";
bool isValidPhone = Regex.IsMatch("13812345678", phonePattern);

替换字符串

string input = "Hello, my name is John Doe.";
string pattern = @"\b[A-Z][a-z]+\b";
string result = Regex.Replace(input, pattern, "***");
Console.WriteLine(result);  // 输出"Hello, my name is *** ***."



5.序列化和反序列化

5.1 二进制序列化

了解即可,.NET Core 3.0+ 和 .NET 5+ 中已弃用

using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

[Serializable] // 必须标记为可序列化
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

// 序列化
Person person = new Person { Name = "张三", Age = 30 };
BinaryFormatter formatter = new BinaryFormatter();

using (FileStream stream = new FileStream("person.dat", FileMode.Create))
{
    formatter.Serialize(stream, person);
}

// 反序列化
using (FileStream stream = new FileStream("person.dat", FileMode.Open))
{
    Person deserializedPerson = (Person)formatter.Deserialize(stream);
    Console.WriteLine($"Name: {deserializedPerson.Name}, Age: {de

serializedPerson.Age}"); }

5.2 XML 序列化

using System.Xml.Serialization;
using System.IO;

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    
    [XmlIgnore] // 忽略此属性
    public string Secret { get; set; }
}

// 序列化
Person person = new Person { Name = "李四", Age = 25, Secret = "123456" };
XmlSerializer serializer = new XmlSerializer(typeof(Person));

using (TextWriter writer = new StreamWriter("person.xml"))
{
    serializer.Serialize(writer, person);
}

// 反序列化
using (TextReader reader = new StreamReader("person.xml"))
{
    Person deserializedPerson = (Person)serializer.Deserialize(reader);
    Console.WriteLine($"Name: {deserializedPerson.Name}, Age: {deserializedPerson.Age}");
}

5.3 JSON 序列化

5.3.1 System.Text.Json

using System.Text.Json;
using System.IO;

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    
    [JsonIgnore] // 忽略此属性
    public string Secret { get; set; }
}

// 序列化
Person person = new Person { Name = "王五", Age = 28, Secret = "654321" };
string jsonString = JsonSerializer.Serialize(person);
File.WriteAllText("person.json", jsonString);

// 反序列化
string jsonFromFile = File.ReadAllText("person.json");
Person deserializedPerson = JsonSerializer.Deserialize<Person>(jsonFromFile);
Console.WriteLine($"Name: {deserializedPerson.Name}, Age: {deserializedPerson.Age}");

// 带选项的序列化
var options = new JsonSerializerOptions
{
    WriteIndented = true, // 格式化输出
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase // 属性名转为驼峰命名
};
string formattedJson = JsonSerializer.Serialize(person, options);

5.3.2 Newtonsoft.Json

需要安装 Newtonsoft.Json

using Newtonsoft.Json;
using System.IO;

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    
    [JsonProperty("fullName")] // 自定义属性名
    public string FullName { get; set; }
}

// 序列化
Person person = new Person { Name = "赵六", Age = 35, FullName = "赵六全名" };
string jsonString = JsonConvert.SerializeObject(person, Formatting.Indented);
File.WriteAllText("person.json", jsonString);

// 反序列化
string jsonFromFile = File.ReadAllText("person.json");
Person deserializedPerson = JsonConvert.DeserializeObject<Person>(jsonFromFile);
Console.WriteLine($"Name: {deserializedPerson.Name}, FullName: {deserializedPerson.FullName}");

5.4 自定义序列化

using System.Text.Json;
using System.Text.Json.Serialization;

public class DateTimeConverter : JsonConverter<DateTime>
{
    private const string Format = "yyyy-MM-dd HH:mm:ss";
    
    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return DateTime.ParseExact(reader.GetString(), Format, null);
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToString(Format));
    }
}

public class Event
{
    public string Name { get; set; }
    
    [JsonConverter(typeof(DateTimeConverter))]
    public DateTime Date { get; set; }
}

// 使用
var options = new JsonSerializerOptions
{
    Converters = { new DateTimeConverter() }
};
string json = JsonSerializer.Serialize(new Event { Name = "会议", Date = DateTime.Now }, options);