C# 教程
C#(读作 C Sharp) 是微软推出的一种现代化、类型安全的编程语言,运行在 .NET 平台上。它语法优雅、功能强大,是 Windows 桌面、Web、游戏(Unity)、云服务开发的主力语言。
本教程内容
| 模块 | 内容 |
|---|---|
| 基础语法 | 变量、类型、运算符、判断、循环 |
| 面向对象 | 类、继承、多态、接口、封装 |
| 高级特性 | 泛型、委托、事件、反射、特性 |
| 实战 | 文件 IO、正则、多线程、集合 |
为什么学 C#
- 跨平台:.NET Core 支持 Windows/macOS/Linux
- 游戏开发:Unity 官方脚本语言
- 企业级:ASP.NET 后端、微服务、云原生
- 语法现代:融合了多种语言的优点
Hello World
using System;
class Program
{
static void Main()
{
Console.WriteLine("Hello, C#!");
}
}
C# 简介
C# 由微软在 2000 年随 .NET Framework 一起发布,由 Anders Hejlsberg 领导设计。它是一门面向对象、类型安全、自动内存管理的现代语言。
核心特点
- 面向对象:类、继承、多态、接口一应俱全
- 类型安全:编译期检查,减少运行时错误
- 自动 GC:垃圾回收,无需手动管理内存
- 语言集成查询(LINQ):优雅地查询集合
- 异步编程:async/await 简洁处理并发
C# 与 .NET 的关系
C# 是语言(语法), .NET 是平台(运行时+类库)
C# 代码 → 编译为 IL(中间语言) → 运行时(JIT)执行
.NET 版本演进:
.NET Framework(Windows) → .NET Core(跨平台) → .NET 5/6/7/8(统一)
C# 能做什么
| 领域 | 技术栈 |
|---|---|
| 桌面应用 | WPF、WinForms |
| Web 后端 | ASP.NET Core |
| 游戏 | Unity |
| 移动应用 | Xamarin / MAUI |
| 云服务 | Azure Functions、微服务 |
开发环境
# 安装 .NET SDK 后验证
dotnet --version
dotnet new console -n Hello # 创建项目
cd Hello
dotnet run # 运行
C# 开发环境
搭建 C# 开发环境:安装 .NET SDK + 选择 IDE,创建并运行第一个项目。
1. 安装 .NET SDK
# Windows/macOS/Linux 都支持
# 到 dotnet.microsoft.com 下载 SDK(LTS 版本)
# 或包管理器安装(macOS):
brew install dotnet-sdk
# 验证
dotnet --version # 如 8.0.100
2. 选择 IDE
| 工具 | 适合 |
|---|---|
| Visual Studio | Windows 全功能 IDE(推荐) |
| VS Code | 轻量 + C# 扩展,跨平台 |
| JetBrains Rider | 专业级,跨平台 |
3. 创建控制台项目
dotnet new console -n MyApp
cd MyApp
code . # VS Code 打开
# 项目结构
# MyApp/
# ├── MyApp.csproj 项目配置
# ├── Program.cs 入口文件
# └── obj/ 编译产物
4. 编写并运行
// Program.cs
Console.WriteLine("你好,世界!");
Console.WriteLine($"今天: {DateTime.Now:yyyy-MM-dd}");
dotnet run
# 输出:
# 你好,世界!
# 今天: 2026-08-17
常用命令
dotnet new console # 新建控制台项目
dotnet new webapi # 新建 Web API 项目
dotnet add package X # 安装 NuGet 包
dotnet build # 编译
dotnet run # 运行
dotnet test # 测试
C# 程序结构
理解 C# 程序的基本结构:命名空间、类、方法、入口点。
程序骨架
using System; // 1. 引用命名空间
namespace MyApp // 2. 定义命名空间
{
class Program // 3. 定义类
{
static void Main() // 4. 入口方法(程序从这里开始)
{
Console.WriteLine("Hello"); // 5. 业务代码
}
}
}
各部分作用
| 部分 | 说明 |
|---|---|
using System; | 引入命名空间,可用 Console 等类型 |
namespace MyApp | 代码的组织容器,避免命名冲突 |
class Program | 类:数据和行为的封装 |
Main() | 程序入口,必须为 static |
简化写法(.NET 6+)
// 顶级语句: 省略 namespace/class/Main
using System;
Console.WriteLine("Hello");
Console.ReadLine(); // 等待输入(防止窗口闪退)
输入与输出
// 输出
Console.WriteLine("带换行输出");
Console.Write("不换行输出");
// 输入
string name = Console.ReadLine(); // 读一行字符串
int age = int.Parse(Console.ReadLine()); // 读数字
Console.WriteLine($"你好, {name}, 年龄 {age}");
编译与运行
C# 源码(.cs) → 编译 → IL 中间语言 → 运行时 JIT → 机器码
dotnet build 编译
dotnet run 编译并运行
C# 基本语法
C# 的基本语法元素:标识符、关键字、注释、语句、块。
标识符与关键字
// 标识符: 变量/方法/类名(字母、数字、下划线, 不能数字开头)
int myVariable = 10;
string UserName = "张三";
// 关键字: 语言保留字, 不能用作标识符
// class, int, string, if, for, using, public, static...
// @ 前缀可把关键字当标识符(不推荐)
string @class = "测试";
注释
// 单行注释
/* 多行
注释 */
/// <summary>XML 文档注释(生成文档用)</summary>
public void Method() { }
变量声明与初始化
// 显式类型
int count = 100;
double price = 99.5;
string name = "C#";
// var: 类型推断(编译期确定)
var age = 25; // int
var city = "北京"; // string
// 常量与只读
const double PI = 3.14159; // 编译期常量
readonly int MaxSize = 100; // 运行时赋值一次
语句与块
// 语句以分号结尾
int x = 1;
// 块: 花括号包裹的语句组
if (x > 0)
{
Console.WriteLine("正数");
}
// 表达式语句: 赋值、调用
x = x + 1;
Console.WriteLine(x);
命名规范
| 类型 | 规范 | 示例 |
|---|---|---|
| 类/方法 | PascalCase | StudentService |
| 变量/参数 | camelCase | userName |
| 常量 | PascalCase | MaxCount |
| 接口 | I + PascalCase | IComparable |
C# 数据类型
C# 数据类型分为值类型与引用类型两大体系。
值类型(存值本身)
// 整数
byte b = 255; // 0~255
short s = -32768; // 16位
int i = -2147483648; // 32位(常用)
long l = 9000000000L; // 64位
// 浮点
float f = 3.14f; // 32位(需 f 后缀)
double d = 3.1415926535; // 64位(默认)
// 其他
decimal money = 99.99m; // 高精度十进制(钱, 用 m)
char c = 'A'; // 单字符
bool ok = true; // 布尔
// 结构体/枚举也是值类型
引用类型(存引用地址)
string name = "Hello"; // 字符串
int[] arr = { 1, 2, 3 }; // 数组
object obj = 42; // 所有类型的基类
class Person { } // 自定义类
Person p = new Person(); // 引用类型实例
// 引用类型: 变量存的是堆上对象的地址
// 赋值时共享同一对象
Person p1 = new Person();
Person p2 = p1; // p1、p2 指向同一对象
值类型 vs 引用类型
| 维度 | 值类型 | 引用类型 |
|---|---|---|
| 存储 | 栈(直接存值) | 堆(存地址) |
| 赋值 | 复制值 | 复制引用 |
| 默认值 | 0/false/null? | null |
| 例子 | int、double、struct | string、class、array |
常用类型速查
int.MaxValue // 2147483647
double.MaxValue // 1.7976931348623157E+308
string.Length // 字符串长度
// 类型转换见下一章
decimal 适合金额(精确),double 适合科学计算(有精度误差)。整数运算不会自动转浮点,需注意除法。C# 类型转换
C# 类型转换:隐式转换、显式转换、Convert 类、Parse/TryParse。
隐式转换(自动)
// 小范围 → 大范围, 安全自动转
int i = 100;
long l = i; // int → long ✅
double d = i; // int → double ✅
float f = 3.5f;
double d2 = f; // float → double ✅
显式转换(强制)
// 大范围 → 小范围, 可能丢失精度, 需强转
double d = 3.99;
int i = (int)d; // 3 (截断小数)
long l = 1000000L;
int i2 = (int)l; // 若超出范围会溢出
// 数值转字符串
string s = i.ToString();
Convert 类(推荐)
// 字符串 → 数值(四舍五入)
string str = "3.7";
double d = Convert.ToDouble(str);
int rounded = Convert.ToInt32(3.7); // 4 (四舍五入)
// 其他
Convert.ToInt32("100"); // "100" → 100
Convert.ToString(123); // 123 → "123"
Convert.ToBoolean("true"); // → true
Convert.ToDateTime("2026-08-17");
Parse / TryParse(字符串→数值)
// Parse: 失败抛异常
int a = int.Parse("42");
double b = double.Parse("3.14");
// TryParse: 失败返回 false, 不抛异常(推荐!)
string input = Console.ReadLine();
if (int.TryParse(input, out int result))
{
Console.WriteLine($"解析成功: {result}");
}
else
{
Console.WriteLine("输入不是数字");
}
装箱与拆箱
// 值类型 → object(装箱): 分配堆内存
int num = 42;
object boxed = num; // 装箱
// object → 值类型(拆箱): 需强转
int unboxed = (int)boxed; // 拆箱
// 泛型可避免装箱(性能更好)
TryParse;数值与字符串互转用 ToString() / Parse;可能丢精度的强转要明确知道后果。C# 变量
深入 C# 变量:声明、初始化、生命周期、var 推断。
变量声明
// 声明 + 初始化
int score = 85;
// 先声明后赋值
int total;
total = 100;
// 多个变量一行
int a = 1, b = 2, c = 3;
// var 类型推断
var message = "Hello"; // string
var numbers = new int[3]; // int[]
变量分类
| 类型 | 位置 | 生命周期 |
|---|---|---|
| 局部变量 | 方法/块内 | 块执行期间 |
| 实例字段 | 类中, 非static | 随对象创建/销毁 |
| 静态字段 | 类中, static | 整个程序生命周期 |
| 常量 | const | 编译期固定 |
示例
class Student
{
// 实例字段: 每个对象一份
public string Name;
// 静态字段: 所有对象共享
public static int Count = 0;
public void SayHello()
{
// 局部变量: 方法内有效
string greeting = $"你好, {Name}";
Console.WriteLine(greeting);
Count++; // 访问静态字段
}
}
变量命名
// 规则
// 1. 字母/数字/下划线, 不能数字开头
// 2. 区分大小写
// 3. 不能是关键字
// 4. 局部变量 camelCase, 字段 PascalCase/_camelCase
int age; // ✅
int _age; // ✅ (私有字段习惯)
int 2age; // ❌ 数字开头
string class; // ❌ 关键字
变量的默认值
// 字段自动有默认值
int n; // 0
double d; // 0
bool b; // false
string s; // null
int[] arr; // null
// 局部变量必须显式赋值才能使用
int x;
// Console.WriteLine(x); // ❌ 编译错误: 未赋值
C# 变量作用域
作用域(Scope)决定变量在哪里可见可用。C# 按块(花括号)划分作用域。
块作用域
public void Demo()
{
int x = 10; // 方法级作用域
if (x > 0)
{
int y = 20; // if 块内作用域
Console.WriteLine(x + y); // ✅ 可见
}
// Console.WriteLine(y); // ❌ y 超出作用域
Console.WriteLine(x); // ✅ 仍可见
}
循环作用域
for (int i = 0; i < 10; i++)
{
// i 在循环内可见
Console.WriteLine(i);
}
// Console.WriteLine(i); // ❌ i 不存在
// 在循环外声明索引
int idx;
for (idx = 0; idx < 10; idx++) { }
Console.WriteLine(idx); // ✅ 10
遮蔽与优先级
class Demo
{
int value = 100; // 字段
public void Test()
{
int value = 5; // 局部变量遮蔽字段
Console.WriteLine(value); // 5 (局部优先)
// 访问字段: 用 this
Console.WriteLine(this.value); // 100
}
}
静态字段作用域
class Counter
{
public static int Count = 0; // 全局共享
public void Inc() { Count++; }
}
// 任何地方都可访问
Counter.Count++;
Console.WriteLine(Counter.Count);
作用域规则速查
| 规则 | 说明 |
|---|---|
| 块内声明 | 只在块内及嵌套块内可见 |
| 同层不可重复声明 | 同名变量在同一作用域报错 |
| 嵌套可见 | 内层可访问外层变量 |
| 局部遮蔽 | 局部优先于字段(用 this 取字段) |
C# 常量
C# 常量:const(编译期)与 readonly(运行时)。
const 常量
// const: 编译期就确定, 必须用字面量初始化
public const double PI = 3.1415926;
public const int MaxUsers = 1000;
public const string Version = "1.0.0";
// 使用
double area = PI * r * r;
Console.WriteLine($"版本: {Version}");
readonly 字段
class Config
{
// readonly: 运行时赋值, 且只能在构造函数或声明时
public readonly string DbConnection;
public readonly int Timeout = 30; // 声明时赋值
public Config(string conn)
{
DbConnection = conn; // 构造函数中赋值
}
// 之后不可再改
// public void Set() { DbConnection = "x"; } // ❌
}
const vs readonly
| 维度 | const | readonly |
|---|---|---|
| 赋值时机 | 编译期(字面量) | 声明或构造函数 |
| 类型限制 | 内置类型(数值/字符串) | 任意类型 |
| 实例相关 | 隐含 static | 可实例或 static |
| 场景 | 数学常量、固定值 | 配置、依赖注入 |
静态常量组(建议)
public static class AppConfig
{
public const string AppName = "知识库";
public const int PageSize = 20;
public static readonly string UploadPath =
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "uploads");
}
// 使用
Console.WriteLine(AppConfig.AppName);
Console.WriteLine(AppConfig.PageSize);
注意事项
// 1. const 必须编译期可计算
// const int X = DateTime.Now.Year; // ❌ 运行时值
// 2. const 编译期替换(引用处直接嵌入值)
// 改 const 需要重新编译所有引用它的程序集
// 3. 字段与局部都用 const
const int DaysPerWeek = 7;
int weeks = 10;
Console.WriteLine(DaysPerWeek * weeks);
static class + const/readonly 统一管理。C# 运算符
C# 运算符:算术、赋值、比较、逻辑、位、三元、空合并。
算术运算符
int a = 10, b = 3;
a + b; // 13 加
a - b; // 7 减
a * b; // 30 乘
a / b; // 3 (整数除法, 截断!)
a % b; // 1 取余
double c = 10.0 / 3; // 3.333... (浮点除法)
a++; // 11 自增
b--; // 2 自减
赋值运算符
int x = 5;
x += 3; // x = x + 3 → 8
x -= 2; // 6
x *= 2; // 12
x /= 4; // 3
x %= 2; // 1
比较与逻辑
int age = 20;
age > 18; // true 大于
age >= 18; // true
age == 18; // false 等于
age != 18; // true 不等于
bool isStudent = true;
bool isVip = false;
isStudent && isVip; // false 与
isStudent || isVip; // true 或
!isStudent; // false 非
三元运算符
int score = 85;
string result = score >= 60 ? "及格" : "不及格";
// 条件 ? 真值 : 假值
位运算符
int flags = 0b0101;
int mask = 0b0011;
flags & mask; // 0b0001 与
flags | mask; // 0b0111 或
flags ^ mask; // 0b0110 异或
~flags; // 取反
flags << 1; // 左移 → 0b1010
flags >> 1; // 右移 → 0b0010
空合并运算符
string name = null;
string display = name ?? "匿名用户"; // "匿名用户"
// ?? : 左边为 null 时用右边
// 空条件运算符
string s = "hello";
int? len = s?.Length; // s 为 null 则整体 null
// 组合
string config = GetConfig() ?? GetDefault() ?? "fallback";
运算符优先级(从高到低)
() → ++ -- → * / % → + - → << >> → 比较 → == != → & → ^ → | → && → || → ?: → 赋值
不确定时用括号!
&&/|| 短路(左侧已定则右侧不执行);?? 是处理 null 的利器。C# 判断
C# 判断语句:if/else、switch、三元,控制程序分支。
if / else if / else
int score = 85;
if (score >= 90)
{
Console.WriteLine("优秀");
}
else if (score >= 60)
{
Console.WriteLine("及格");
}
else
{
Console.WriteLine("不及格");
}
嵌套与简化
// 嵌套(可读性差, 尽量避免过深)
bool isVip = true;
int amount = 500;
if (isVip)
{
if (amount > 300)
{
Console.WriteLine("VIP 大客户");
}
}
// 合并条件
if (isVip && amount > 300)
{
Console.WriteLine("VIP 大客户");
}
// 单语句可省略花括号(建议保留)
if (score >= 60) Console.WriteLine("通过");
switch 语句
string status = "pending";
switch (status)
{
case "pending":
Console.WriteLine("待处理");
break;
case "approved":
Console.WriteLine("已批准");
break;
case "rejected":
Console.WriteLine("已驳回");
break;
default:
Console.WriteLine("未知状态");
break;
}
switch 表达式(C# 8+)
// 更简洁的写法
string status = "done";
string text = status switch
{
"pending" => "待处理",
"done" => "已完成",
"failed" => "失败",
_ => "未知" // default
};
Console.WriteLine(text); // 已完成
模式匹配
// 类型模式
object obj = 42;
if (obj is int number)
{
Console.WriteLine($"是整数: {number}");
}
// 属性模式
var user = new { Name = "张三", Age = 25 };
if (user is { Age: >= 18 and < 60 })
{
Console.WriteLine("成年且未退休");
}
// 关系模式
int score = 85;
string grade = score switch
{
>= 90 => "A",
>= 80 => "B",
>= 70 => "C",
_ => "D"
};
switch 表达式(简洁、无 break);条件复杂用「合并 + 提前 return」减少嵌套层级。C# 循环
C# 循环:for、foreach、while、do-while 以及 break/continue。
for 循环
// 打印 1~5
for (int i = 1; i <= 5; i++)
{
Console.WriteLine(i);
}
// 递减
for (int i = 10; i > 0; i -= 2)
{
Console.WriteLine(i); // 10 8 6 4 2
}
// 循环数组
int[] nums = { 10, 20, 30 };
for (int i = 0; i < nums.Length; i++)
{
Console.WriteLine(nums[i]);
}
foreach 循环(遍历集合)
// 数组
foreach (int n in nums)
{
Console.WriteLine(n);
}
// 字符串(字符遍历)
foreach (char c in "hello")
{
Console.WriteLine(c);
}
// 字典
var dict = new Dictionary
{
["apple"] = 5,
["banana"] = 3
};
foreach (var kv in dict)
{
Console.WriteLine($"{kv.Key}: {kv.Value}");
}
while 循环
int n = 0;
while (n < 5)
{
Console.WriteLine(n);
n++;
}
// 读取输入直到 q
string input = "";
while (input != "q")
{
input = Console.ReadLine();
Console.WriteLine($"输入了: {input}");
}
do-while(至少执行一次)
int guess;
do
{
Console.Write("猜数字(1-10): ");
guess = int.Parse(Console.ReadLine());
} while (guess != 7);
Console.WriteLine("猜对了!");
break / continue
// break: 跳出整个循环
for (int i = 1; i <= 10; i++)
{
if (i == 5) break;
Console.WriteLine(i); // 1 2 3 4
}
// continue: 跳过本次
for (int i = 1; i <= 10; i++)
{
if (i % 2 == 0) continue; // 跳过偶数
Console.WriteLine(i); // 1 3 5 7 9
}
// 嵌套循环 break 只跳出内层
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
if (j == 1) break; // 只跳出 j 循环
Console.Write($"{i}{j} ");
}
}
循环场景速查
| 场景 | 推荐 |
|---|---|
| 知道次数 | for |
| 遍历集合元素 | foreach |
| 不确定次数 | while |
| 至少执行一次 | do-while |
C# 封装
封装(Encapsulation):把数据与操作数据的方法捆绑在类中,并用访问修饰符控制外部可见性——面向对象三大特性之一。
访问修饰符
| 修饰符 | 可见范围 |
|---|---|
public | 任何地方 |
private | 仅本类 |
protected | 本类 + 派生类 |
internal | 同一程序集 |
protected internal | 本程序集或派生类 |
封装示例
public class BankAccount
{
// 私有字段: 外部不能直接访问
private decimal _balance;
private readonly string _accountNo;
// 构造
public BankAccount(string accountNo)
{
_accountNo = accountNo;
_balance = 0;
}
// 公开方法: 受控的修改入口
public void Deposit(decimal amount)
{
if (amount <= 0)
throw new ArgumentException("金额必须大于0");
_balance += amount;
}
public bool Withdraw(decimal amount)
{
if (amount > _balance)
return false; // 余额不足
_balance -= amount;
return true;
}
// 只读属性
public decimal Balance => _balance;
}
// 使用
var acc = new BankAccount("6222-0001");
acc.Deposit(1000);
bool ok = acc.Withdraw(300);
Console.WriteLine(acc.Balance); // 700
// acc._balance = 9999; // ❌ 私有不可访问
属性(Property)封装
public class Person
{
private string _name = "";
// 属性: 读写的封装
public string Name
{
get { return _name; }
set
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("姓名不能为空");
_name = value;
}
}
// 自动属性(简化)
public int Age { get; set; }
// 只读计算属性
public bool IsAdult => Age >= 18;
}
为什么要封装
- 保护数据:防止非法状态(如负余额)
- 隐藏实现:内部改了,外部调用不变
- 控制入口:加校验、日志、权限
private,需要对外暴露时才加属性;get 尽量保留(读取更安全),写操作谨慎开放。C# 方法
C# 方法:定义、参数传递、重载、可选参数。
方法定义
// 返回类型 方法名(参数) { 主体 }
public int Add(int a, int b)
{
return a + b;
}
// 无返回值
public void Print(string msg)
{
Console.WriteLine(msg);
}
// 调用
int sum = Add(3, 4);
Print($"和是 {sum}");
参数传递
// 值传递(默认): 修改不影响原变量
void Change(int x) { x = 100; }
int a = 5;
Change(a);
Console.WriteLine(a); // 5 (不变)
// ref: 引用传递, 可修改原变量
void ChangeRef(ref int x) { x = 100; }
int b = 5;
ChangeRef(ref b);
Console.WriteLine(b); // 100 (已改)
// out: 输出参数, 必须赋值
bool TryDivide(int a, int b, out int result)
{
if (b == 0) { result = 0; return false; }
result = a / b;
return true;
}
if (TryDivide(10, 3, out int r))
Console.WriteLine(r); // 3
方法重载
// 同名方法, 不同参数列表
public int Add(int a, int b) => a + b;
public double Add(double a, double b) => a + b;
public int Add(int a, int b, int c) => a + b + c;
Console.WriteLine(Add(1, 2)); // 3
Console.WriteLine(Add(1.5, 2.5)); // 4.0
Console.WriteLine(Add(1, 2, 3)); // 6
可选参数与命名参数
// 可选参数: 必须放在末尾
public void SendEmail(string to, string subject = "通知",
bool html = true)
{
// ...
}
SendEmail("a@b.com"); // 用默认值
SendEmail("a@b.com", "重要通知"); // 指定subject
SendEmail("a@b.com", html: false); // 命名参数
// params: 可变参数
public int Sum(params int[] numbers)
{
return numbers.Sum();
}
Sum(1, 2, 3, 4); // 10
Sum(); // 0
表达式体方法(简洁)
public int Square(int x) => x * x;
public void Greet() => Console.WriteLine("你好");
public string FullName => $"{FirstName} {LastName}";
C# 可空类型
C# 可空类型:Nullable<T>、? 后缀、空值处理。
可空值类型
// 值类型默认不能为 null, 加 ? 后可以
int? age = null; // 可空 int
double? price = 99.5; // 可空 double
bool? isActive = null; // 可空 bool(三态: true/false/null)
// Nullable 等价写法
Nullable n = 5;
常用操作
int? score = 85;
// 判断是否有值
if (score.HasValue)
{
Console.WriteLine(score.Value); // 85
}
// 简化
if (score is int v)
{
Console.WriteLine(v);
}
// 取值: 有值取之, 无值取默认
int result = score.GetValueOrDefault(); // 85
int result2 = score.GetValueOrDefault(0); // 无值则 0
// ?? 空合并
int final = score ?? 0; // 无值用 0
可空引用类型(C# 8+)
// 在 csproj 启用: enable
// 之后编译器会警告可能为 null 的使用
string name = null; // 警告: 非空声明赋了 null
string? maybe = null; // ✅ 明确可空
// 使用可空引用
string? userInput = Console.ReadLine();
int len = userInput!.Length; // !: 告诉编译器"我确定非空"
// 或安全处理:
if (userInput is not null)
{
Console.WriteLine(userInput.Length);
}
可空与数据库/表单
// 数据库字段 NULL → 可空类型
public class User
{
public int Id { get; set; } // NOT NULL
public string? Nickname { get; set; } // 可为 NULL
public DateTime? LastLogin { get; set; } // 可为 NULL
}
// 使用
User u = GetUser();
if (u.LastLogin.HasValue)
{
Console.WriteLine($"上次登录: {u.LastLogin.Value:yyyy-MM-dd}");
}
注意事项
// 不要对 null 调用 .Value (会抛异常)
int? x = null;
// int v = x.Value; // ❌ InvalidOperationException
// 先判 HasValue 或用 ??
// 可空类型参与运算
int? a = 10, b = null;
int? sum = a + b; // null (任一为 null 结果 null)
??、?.、GetValueOrDefault 优雅处理空值,避免 NRE。C# 数组(Array)
C# 数组:一维、多维、交错数组以及常用方法。
一维数组
// 声明方式
int[] arr1 = new int[5]; // 长度5, 默认0
int[] arr2 = { 1, 2, 3 }; // 初始化
int[] arr3 = new int[] { 4, 5, 6 };
string[] names = { "张三", "李四" };
// 访问
Console.WriteLine(arr2[0]); // 1
arr2[2] = 30; // 修改
Console.WriteLine(arr2.Length); // 3
// 遍历
foreach (int n in arr2) { Console.WriteLine(n); }
for (int i = 0; i < arr2.Length; i++) { }
常用方法
using System.Linq;
int[] nums = { 5, 3, 8, 1, 9 };
Array.Sort(nums); // 排序 [1,3,5,8,9]
Array.Reverse(nums); // 反转
Array.IndexOf(nums, 8); // 找下标(找不到 -1)
nums.Sum(); // 26 (LINQ)
nums.Max(); // 9
nums.Min(); // 1
nums.Average(); // 5.2
nums.Where(n => n > 3); // [5,8,9] (筛选)
nums.OrderBy(n => n); // 排序(不修改原数组)
多维数组
// 矩形多维数组
int[,] matrix = new int[2, 3]
{
{ 1, 2, 3 },
{ 4, 5, 6 }
};
Console.WriteLine(matrix[1, 2]); // 6
Console.WriteLine(matrix.GetLength(0)); // 2 (行)
Console.WriteLine(matrix.GetLength(1)); // 3 (列)
// 遍历
for (int i = 0; i < matrix.GetLength(0); i++)
for (int j = 0; j < matrix.GetLength(1); j++)
Console.Write(matrix[i, j] + " ");
交错数组(数组的数组)
int[][] jagged = new int[3][];
jagged[0] = new int[] { 1, 2 };
jagged[1] = new int[] { 3, 4, 5, 6 };
jagged[2] = new int[] { 7 };
Console.WriteLine(jagged[1][3]); // 6 (行可不同长度)
foreach (var row in jagged)
{
foreach (var v in row)
Console.Write(v + " ");
Console.WriteLine();
}
复制与克隆
int[] original = { 1, 2, 3 };
int[] copy = (int[])original.Clone(); // 值复制
copy[0] = 99;
Console.WriteLine(original[0]); // 1 (不受影响)
// 部分复制
int[] part = new int[2];
Array.Copy(original, 1, part, 0, 2); // [2,3]
List<T>。数组是引用类型:直接赋值是共享引用,要复制数据用 Clone()。C# 字符串(String)
C# 字符串:不可变、常用方法、格式化、StringBuilder。
字符串基础
string s1 = "Hello";
string s2 = @"C:\path oile"; // 原义字符串(不转义)
string s3 = $"{s1} World"; // 字符串插值
// 字符串不可变: 每次修改都生成新对象
string a = "Hello";
string b = a + " World"; // 新字符串
string c = a.ToUpper(); // 新字符串
Console.WriteLine(a); // Hello (原样)
常用方法
string text = "Hello, World";
text.Length; // 12
text.ToUpper(); // "HELLO, WORLD"
text.ToLower(); // "hello, world"
text.Trim(); // 去首尾空格
text.StartsWith("Hello"); // true
text.EndsWith("d"); // true
text.Contains("World"); // true
text.IndexOf("World"); // 7
text.Substring(7); // "World"
text.Substring(0, 5); // "Hello"
text.Replace("World", "C#"); // "Hello, C#"
text.Split(','); // ["Hello", " World"]
string.Join("-", "a", "b", "c"); // "a-b-c"
字符串比较与判空
string x = "abc";
string y = "ABC";
x == y; // false (区分大小写)
x.Equals(y, StringComparison.OrdinalIgnoreCase); // true
// 判空(推荐)
string? s = null;
string.IsNullOrEmpty(s); // true
string.IsNullOrWhiteSpace(" "); // true
// 可空字符串使用
string? name = Console.ReadLine();
if (string.IsNullOrWhiteSpace(name))
{
Console.WriteLine("输入为空");
}
字符串插值与格式化
string name = "张三";
int age = 25;
double score = 92.5;
// 插值
Console.WriteLine($"姓名: {name}, 年龄: {age}");
// 格式化
Console.WriteLine($"{score:F1}"); // 92.5
Console.WriteLine($"{score:P0}"); // 9,250%
Console.WriteLine($"{123456:N0}"); // 123,456
Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm}");
Console.WriteLine($"{3.14159:F2}"); // 3.14
// 对齐
Console.WriteLine($"{"左对齐",-10}|");
Console.WriteLine($"{"右对齐",10}|");
StringBuilder(频繁拼接用)
using System.Text;
// 大量拼接用 StringBuilder(避免创建大量临时字符串)
var sb = new StringBuilder();
for (int i = 0; i < 1000; i++)
{
sb.Append(i).Append(", ");
}
string result = sb.ToString();
+ 或插值;循环/大量拼接用 StringBuilder;用户输入判空用 IsNullOrWhiteSpace。C# 结构体(Struct)
结构体(struct):轻量级的值类型,适合表示小型的、不可变的数据片段。
struct 定义
public struct Point
{
public int X;
public int Y;
// 构造函数(C# 10+ 支持无参构造)
public Point(int x, int y)
{
X = x;
Y = y;
}
public double Distance()
{
return Math.Sqrt(X * X + Y * Y);
}
}
// 使用
Point p = new Point(3, 4);
Console.WriteLine(p.Distance()); // 5
p.X = 10; // 可修改(除非 readonly)
readonly struct
// 只读结构体: 所有成员只读, 更安全高效
public readonly struct Money
{
public decimal Amount { get; }
public string Currency { get; }
public Money(decimal amount, string currency)
{
Amount = amount;
Currency = currency;
}
public Money Add(Money other) => new Money(Amount + other.Amount, Currency);
}
struct vs class
| 维度 | struct | class |
|---|---|---|
| 类型 | 值类型(栈) | 引用类型(堆) |
| 赋值 | 复制值 | 共享引用 |
| 继承 | 不支持 | 支持 |
| 默认值 | 字段默认0/false | null |
| 性能 | 小对象快(无GC) | 有GC开销 |
| 场景 | Point、Color、坐标 | 业务实体 |
赋值行为差异
// struct: 复制
var p1 = new Point(1, 2);
var p2 = p1;
p2.X = 99;
Console.WriteLine(p1.X); // 1 (p1 不受影响)
// class: 共享
var r1 = new Rectangle();
var r2 = r1;
r2.Width = 50;
Console.WriteLine(r1.Width); // 50 (共享同一对象)
record struct(C# 10+)
// 值语义 + 值相等比较
public readonly record struct Temperature(double Value, string Unit);
var t1 = new Temperature(36.5, "C");
var t2 = new Temperature(36.5, "C");
Console.WriteLine(t1 == t2); // true (按值比较)
C# 枚举(Enum)
枚举(enum):定义一组具名的常量,提高代码可读性与安全性。
枚举定义
public enum OrderStatus
{
Pending = 0, // 待处理
Paid = 1, // 已支付
Shipped = 2, // 已发货
Completed = 3, // 已完成
Cancelled = 4 // 已取消
}
// 不指定值时从 0 开始递增
public enum Color { Red, Green, Blue } // 0,1,2
// 指定基础类型(默认 int)
public enum Level : byte { Low = 1, Mid = 2, High = 3 }
使用枚举
OrderStatus status = OrderStatus.Paid;
// 比较
if (status == OrderStatus.Paid)
{
Console.WriteLine("已支付");
}
// switch 处理
string text = status switch
{
OrderStatus.Pending => "待处理",
OrderStatus.Paid => "已支付",
OrderStatus.Shipped => "已发货",
_ => "其他"
};
// 转字符串/数字
string name = status.ToString(); // "Paid"
int value = (int)status; // 1
// 字符串/数字 → 枚举
OrderStatus s2 = (OrderStatus)1; // Paid
bool ok = Enum.TryParse("Shipped", out OrderStatus s3); // true
枚举与显示名称
public enum Priority
{
[Description("低优先级")]
Low,
[Description("中优先级")]
Mid,
[Description("高优先级")]
High
}
// 读取描述
using System.ComponentModel;
static string GetDescription(Enum value)
{
var field = value.GetType().GetField(value.ToString());
var attr = (DescriptionAttribute?)Attribute.GetCustomAttribute(
field!, typeof(DescriptionAttribute));
return attr?.Description ?? value.ToString();
}
Console.WriteLine(GetDescription(Priority.High)); // 高优先级
[Flags] 位标志枚举
[Flags]
public enum Permissions
{
None = 0,
Read = 1,
Write = 2,
Execute = 4,
All = Read | Write | Execute
}
var perm = Permissions.Read | Permissions.Write; // 组合
perm.HasFlag(Permissions.Read); // true
perm.HasFlag(Permissions.Execute); // false
// 添加/移除
perm |= Permissions.Execute; // 加
perm &= ~Permissions.Write; // 移除 Write
[Flags] 用 2 的幂定义组合权限;展示用 Description 属性配合中文。C# 类(Class)
类(class)是 C# 面向对象的核心:封装数据(字段/属性)与行为(方法)。
类定义
public class Car
{
// 字段
private string _brand;
// 属性
public string Brand
{
get => _brand;
set => _brand = value ?? "未知";
}
public int Speed { get; private set; } // 外部只读
// 构造函数
public Car(string brand)
{
_brand = brand;
Speed = 0;
}
// 方法
public void Accelerate(int delta)
{
Speed += delta;
}
public void Brake()
{
Speed = Math.Max(0, Speed - 10);
}
}
创建与使用对象
// new 创建对象
Car myCar = new Car("比亚迪");
myCar.Accelerate(50);
myCar.Brake();
Console.WriteLine($"{myCar.Brand} 速度 {myCar.Speed}");
// 对象初始化器
var user = new User
{
Name = "张三",
Age = 25
};
构造函数的几种形态
public class Product
{
public string Name { get; }
public decimal Price { get; }
// 默认/主构造函数
public Product(string name, decimal price)
{
Name = name;
Price = price;
}
// 重载构造函数(链式调用)
public Product(string name) : this(name, 0m) { }
// 静态工厂
public static Product CreateDefault() => new("未命名", 0m);
}
var p = Product.CreateDefault();
静态成员
public class MathUtils
{
public const double Pi = 3.14159; // 常量
public static int Count = 0; // 静态字段
public static int Square(int x) => x * x; // 静态方法
static MathUtils() { Count = 0; } // 静态构造
}
// 通过类名访问(无需实例)
int sq = MathUtils.Square(5);
MathUtils.Count++;
this 与对象引用
public class Person
{
private string _name;
public Person(string name)
{
this._name = name; // this 区分字段与参数
}
public Person Clone()
{
return new Person(this._name); // this 指当前对象
}
}
C# 继承
继承(Inheritance):派生类复用并扩展基类的代码,实现「is-a」关系——面向对象三大特性之二。
基础继承
public class Animal
{
public string Name { get; set; } = "";
public Animal(string name) { Name = name; }
public virtual void Speak() // virtual: 可重写
{
Console.WriteLine($"{Name} 发出声音");
}
}
public class Dog : Animal // : 表示继承
{
public Dog(string name) : base(name) { } // base: 调基类构造
public override void Speak() // override: 重写
{
Console.WriteLine($"{Name} 汪汪叫");
}
public void Fetch() { Console.WriteLine("叼回飞盘"); }
}
// 使用
Dog dog = new Dog("旺财");
dog.Speak(); // 旺财 汪汪叫
dog.Fetch(); // 叼回飞盘 (子类特有)
继承层级与多态引用
Animal a = new Dog("小黑"); // 基类引用指向子类对象
a.Speak(); // 小黑 汪汪叫 (多态: 调用实际类型的方法)
Animal[] animals = {
new Dog("A"),
new Cat("B"),
new Animal("C")
};
foreach (var animal in animals)
{
animal.Speak(); // 各自实现
}
sealed 与 abstract
// abstract: 抽象类, 不能实例化, 子类必须实现抽象成员
public abstract class Shape
{
public abstract double Area(); // 抽象方法(无实现)
public void Describe() // 普通方法
{
Console.WriteLine($"面积: {Area()}");
}
}
public class Circle : Shape
{
public double Radius { get; }
public Circle(double r) { Radius = r; }
public override double Area() => Math.PI * Radius * Radius;
}
// sealed: 密封类, 不能被继承
public sealed class FinalConfig { }
// public class Sub : FinalConfig { } // ❌ 编译错误
base 关键字
public class BaseClass
{
protected int Value = 10;
public virtual void Show()
{
Console.WriteLine($"Base: {Value}");
}
}
public class Derived : BaseClass
{
public override void Show()
{
base.Show(); // 调用基类实现
Console.WriteLine($"Derived: {Value}"); // 访问受保护成员
}
}
is / as 类型判断
object obj = new Dog("阿黄");
if (obj is Dog dog1) // 模式匹配
{
dog1.Fetch();
}
Dog? d = obj as Dog; // as: 转换失败返回 null
if (d != null) { d.Fetch(); }
Animal a2 = new Dog("小黄");
if (a2 is Dog) { Console.WriteLine("是 Dog"); }
C# 多态性
多态(Polymorphism):同一接口、不同实现——「一个方法,多种形态」。核心机制是 virtual/override。
多态的三种形式
1. 方法重载(编译时多态): 同名方法不同参数
2. 继承重写(运行时多态): virtual + override
3. 接口实现: 不同类实现同一接口
运行时多态
public class Shape
{
public virtual void Draw()
{
Console.WriteLine("绘制形状");
}
}
public class Circle : Shape
{
public override void Draw()
{
Console.WriteLine("绘制圆形");
}
}
public class Rectangle : Shape
{
public override void Draw()
{
Console.WriteLine("绘制矩形");
}
}
// 多态: 运行时决定调用哪个 Draw
Shape[] shapes = { new Circle(), new Rectangle(), new Shape() };
foreach (var s in shapes)
{
s.Draw();
}
// 输出:
// 绘制圆形
// 绘制矩形
// 绘制形状
virtual / override / new
public class Base
{
public virtual void Method() => Console.WriteLine("Base");
}
public class Derived : Base
{
// override: 真正重写(推荐)
public override void Method() => Console.WriteLine("Derived");
// new: 隐藏基类方法(不推荐, 破坏多态)
public new void Method() => Console.WriteLine("隐藏");
}
// override 效果: 基类引用调子类实现
Base b = new Derived();
b.Method(); // Derived (多态生效)
// new 效果: 基类引用调基类实现
// (Base b = new Derived(); b.Method() → Base)
抽象类与多态
public abstract class Payment
{
public abstract void Pay(decimal amount); // 子类实现
public void Confirm()
{
Console.WriteLine("支付确认");
}
}
public class WeChatPay : Payment
{
public override void Pay(decimal amount)
=> Console.WriteLine($"微信支付 {amount} 元");
}
public class Alipay : Payment
{
public override void Pay(decimal amount)
=> Console.WriteLine($"支付宝支付 {amount} 元");
}
// 统一处理
void Checkout(Payment payment, decimal amount)
{
payment.Pay(amount); // 多态
payment.Confirm();
}
Checkout(new WeChatPay(), 99);
Checkout(new Alipay(), 99);
多态的价值
- 开闭原则:加新类型不改已有代码(加个新 Payment 子类即可)
- 统一接口:调用方只需面向基类/接口编程
- 可扩展:策略模式、工厂模式的根基
C# 运算符重载
运算符重载:为自定义类型定义运算符的行为(如 +、-、==),让代码更自然。
定义运算符重载
public struct Vector
{
public double X { get; }
public double Y { get; }
public Vector(double x, double y) { X = x; Y = y; }
// 重载 +
public static Vector operator +(Vector a, Vector b)
=> new Vector(a.X + b.X, a.Y + b.Y);
// 重载 -
public static Vector operator -(Vector a, Vector b)
=> new Vector(a.X - b.X, a.Y - b.Y);
// 重载 * (向量乘标量)
public static Vector operator *(Vector a, double s)
=> new Vector(a.X * s, a.Y * s);
// 重载 == 和 != (应配套)
public static bool operator ==(Vector a, Vector b)
=> a.X == b.X && a.Y == b.Y;
public static bool operator !=(Vector a, Vector b)
=> !(a == b);
// 同时应重写 Equals 和 GetHashCode
public override bool Equals(object? obj)
=> obj is Vector v && this == v;
public override int GetHashCode() => (X, Y).GetHashCode();
public override string ToString() => $"({X}, {Y})";
}
// 使用
var v1 = new Vector(1, 2);
var v2 = new Vector(3, 4);
Vector sum = v1 + v2; // (4, 6)
Vector scaled = v1 * 2.5; // (2.5, 5)
Console.WriteLine(v1 == new Vector(1, 2)); // True
Console.WriteLine(sum); // (4, 6)
可重载的运算符
| 类别 | 运算符 |
|---|---|
| 算术 | + - * / % |
| 比较 | == != < > <= >=(成对) |
| 一元 | ! ++ -- - + |
| 转换 | implicit / explicit |
隐式/显式转换
public struct Celsius
{
public double Value { get; }
public Celsius(double v) { Value = v; }
// 隐式: double → Celsius
public static implicit operator Celsius(double v)
=> new Celsius(v);
// 显式: Celsius → Fahrenheit
public static explicit operator Fahrenheit(Celsius c)
=> new Fahrenheit(c.Value * 9 / 5 + 32);
}
public struct Fahrenheit
{
public double Value { get; }
public Fahrenheit(double v) { Value = v; }
}
Celsius c = 36.5; // 隐式转换
Fahrenheit f = (Fahrenheit)c; // 显式转换
注意事项
// 1. 重载 == 必须同时重写 Equals 和 GetHashCode
// 2. 重载 < 必须同时重载 >
// 3. 运算符重载不能改变原有语义(如 + 不能做减法)
// 4. 滥用会降低可读性
C# 接口(Interface)
接口(interface)定义一组「能力契约」:只声明成员,不实现。任何实现接口的类都必须提供这些成员。
接口定义与实现
public interface ILogger
{
void Log(string message); // 方法签名
string Name { get; } // 属性
}
public class ConsoleLogger : ILogger
{
public string Name => "Console";
public void Log(string message)
{
Console.WriteLine($"[{Name}] {message}");
}
}
public class FileLogger : ILogger
{
public string Name => "File";
public void Log(string message)
{
File.AppendAllText("log.txt", message + "\n");
}
}
面向接口编程
// 依赖接口, 不依赖具体类
void WriteLog(ILogger logger, string msg)
{
logger.Log(msg); // 多态
}
var logger = new ConsoleLogger();
WriteLog(logger, "用户登录"); // [Console] 用户登录
// 轻松切换实现(可测试性)
WriteLog(new FileLogger(), "审计记录");
接口默认实现(C# 8+)
public interface IReport
{
string Title { get; }
string Render();
// 默认实现: 实现类可不重写
public void Print() => Console.WriteLine(Render());
}
public class SalesReport : IReport
{
public string Title => "销售报表";
public string Render() => "| 1月 | 2月 | 3月 |";
}
var report = new SalesReport();
report.Print(); // 用默认实现
多接口实现
public interface ISaveable { void Save(); }
public interface IPrintable { void Print(); }
public class Document : ISaveable, IPrintable
{
public void Save() => Console.WriteLine("保存文档");
public void Print() => Console.WriteLine("打印文档");
}
// 接口引用
ISaveable s = new Document();
s.Save();
IPrintable p = new Document();
p.Print();
接口 vs 抽象类
| 维度 | interface | abstract class |
|---|---|---|
| 成员 | 纯契约(默认无实现) | 可有实现字段 |
| 继承数量 | 可实现多个 | 只能继承一个 |
| 字段 | 不能有实例字段 | 可以有 |
| 场景 | 能力/契约(ILogger) | 家族层级(Shape) |
接口设计原则
// 接口隔离: 小而专(不要大而全)
public interface IReadable { string Read(); }
public interface IWritable { void Write(string data); }
// 实现需要的能力
public class FileStore : IReadable, IWritable { ... }
public class ReadOnlyCache : IReadable { ... } // 只读场景
C# 命名空间(Namespace)
命名空间(namespace)组织代码、避免命名冲突,是 C# 代码的「文件夹」。
定义与使用
namespace Company.Project.Core
{
public class Config { }
}
namespace Company.Project.Api
{
public class Program
{
static void Main()
{
// 用 using 引入
// using Company.Project.Core;
var cfg = new Config();
}
}
}
using 语句
// 文件顶部引入
using System;
using System.Collections.Generic;
using System.Linq;
// 别名(解决冲突)
using Console = System.Console;
// 静态引入
using static System.Math;
double x = Sqrt(16); // 直接用方法名
// 全局 using(所有文件生效, 写在任意文件的顶部+global)
global using System;
嵌套命名空间与别名
// 嵌套
namespace Outer
{
namespace Inner
{
public class Helper { }
}
}
// 完整名: Outer.Inner.Helper
// 点语法等价
namespace Outer.Inner
{
public class Helper2 { }
}
// 别名
using HelperAlias = Outer.Inner.Helper;
var h = new HelperAlias();
命名空间 vs 程序集
命名空间(逻辑组织) ≠ 程序集(物理文件 .dll)
- 一个程序集可含多个命名空间
- 一个命名空间可跨多个程序集
命名空间约定: 公司.项目.模块
如: MyCompany.Blog.Domain, MyCompany.Blog.Services
文件作用域命名空间(C# 10+)
// 新写法: 不用花括号
namespace MyApp.Models;
public class User
{
public int Id { get; set; }
public string Name { get; set; } = "";
}
C# 预处理器指令
预处理器指令:编译前的条件编译指令(以 # 开头),用于按条件包含代码、定义符号、区域折叠。
条件编译
// 定义符号
#define DEBUG
// #undef DEBUG
class Program
{
static void Main()
{
#if DEBUG
Console.WriteLine("调试模式");
#else
Console.WriteLine("发布模式");
#endif
}
}
常用指令
| 指令 | 作用 |
|---|---|
#define / #undef | 定义/取消符号 |
#if / #elif / #else / #endif | 条件编译 |
#region / #endregion | 代码折叠区域 |
#warning / #error | 编译警告/错误 |
#nullable | 控制可空上下文 |
#pragma warning | 抑制警告 |
平台/框架条件编译
class PlatformHelper
{
public static string GetPath()
{
#if WINDOWS
return @"C:\temp";
#elif LINUX || MACOS
return "/tmp";
#else
return ".";
#endif
}
}
// 项目文件(.csproj)中定义符号:
//
// $(DefineConstants);WINDOWS
//
#region 折叠
class BigService
{
#region 私有方法
private void Helper1() { }
private void Helper2() { }
#endregion
#region 公开 API
public void DoWork() { }
#endregion
}
调试辅助
// 仅调试版执行的代码
#if DEBUG
Console.WriteLine($"耗时: {sw.ElapsedMilliseconds}ms");
#endif
// 条件特性
[System.Diagnostics.Conditional("DEBUG")]
void LogDebug(string msg) { Console.WriteLine(msg); }
// 抑制警告
#pragma warning disable CS0219 // 未使用变量警告
int unused = 5;
#pragma warning restore CS0219
#if DEBUG 的代码不会出现在 Release 程序集中(更省体积)。C# 正则表达式
正则表达式(Regex):用模式匹配文本,用于校验、提取、替换。
基础匹配
using System.Text.RegularExpressions;
string text = "我的邮箱是 test@example.com,电话 138-1234-5678";
// 匹配邮箱
Match m = Regex.Match(text, @"[\w.-]+@[\w.-]+\.\w+");
if (m.Success)
{
Console.WriteLine(m.Value); // test@example.com
}
// 判断是否匹配
bool isMatch = Regex.IsMatch("abc123", @"\d+"); // true
常用验证
// 邮箱
Regex.IsMatch(email, @"^[\w.-]+@[\w.-]+\.\w+$");
// 手机号(中国大陆)
Regex.IsMatch(phone, @"^1[3-9]\d{9}$");
// 身份证
Regex.IsMatch(id, @"^\d{17}[0-9Xx]$");
// URL
Regex.IsMatch(url, @"^https?://");
// 中文
Regex.IsMatch(text, @"[\u4e00-\u9fa5]");
提取与分组
string html = "价格: 99.5 元
";
Match m = Regex.Match(html, @"(\d+\.?\d*)");
if (m.Success)
{
string price = m.Groups[1].Value; // 99.5 (分组1)
Console.WriteLine(price);
}
// 多个匹配
string log = "2026-01-01 10:00 | 2026-02-02 11:30";
var dates = Regex.Matches(log, @"\d{4}-\d{2}-\d{2}");
foreach (Match d in dates)
{
Console.WriteLine(d.Value); // 2026-01-01, 2026-02-02
}
替换与分割
// 替换
string masked = Regex.Replace(
"13812345678",
@"(\d{3})\d{4}(\d{4})",
"$1****$2"); // 138****5678
// 清理空白
string clean = Regex.Replace("a b c", @"\s+", " "); // "a b c"
// 分割
var parts = Regex.Split("a,b;c|d", @"[,;|]");
// ["a", "b", "c", "d"]
性能与预编译
// 频繁使用: 缓存正则(避免每次编译)
private static readonly Regex EmailRegex =
new(@"^[\w.-]+@[\w.-]+\.\w+$",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
bool IsValidEmail(string email) => EmailRegex.IsMatch(email);
// 超时保护(防 ReDoS)
new Regex(pattern, RegexOptions.None, TimeSpan.FromSeconds(1));
\d数字、\w字母数字、.+任意、^$边界、()分组、{n,m}次数。复杂正则先在在线工具测试。C# 异常处理
异常处理:用 try/catch/finally 捕获与处理运行时错误,保证程序健壮性。
基本结构
try
{
int x = 10;
int y = 0;
int result = x / y; // 抛 DivideByZeroException
}
catch (DivideByZeroException ex)
{
Console.WriteLine($"除零错误: {ex.Message}");
}
catch (Exception ex) // 兜底: 其他所有异常
{
Console.WriteLine($"未知错误: {ex.Message}");
}
finally
{
Console.WriteLine("总会执行(清理资源)"); // 可选
}
常见异常类型
| 异常 | 场景 |
|---|---|
NullReferenceException | 访问 null 对象 |
ArgumentException | 参数非法 |
FormatException | 格式转换失败 |
IndexOutOfRangeException | 索引越界 |
FileNotFoundException | 文件不存在 |
InvalidOperationException | 状态非法 |
TimeoutException | 超时 |
抛出异常
public void Withdraw(decimal amount)
{
if (amount <= 0)
throw new ArgumentException("金额必须大于0", nameof(amount));
if (amount > Balance)
throw new InvalidOperationException("余额不足");
}
// 自定义异常
public class OrderException : Exception
{
public string OrderId { get; }
public OrderException(string orderId, string message)
: base(message)
{
OrderId = orderId;
}
}
using 资源管理
// 等价于 try/finally + Dispose
using (var reader = new StreamReader("file.txt"))
{
string content = reader.ReadToEnd();
} // 自动释放
// 简化写法(C# 8+)
using var writer = new StreamWriter("out.txt");
writer.WriteLine("hello");
// 作用域结束时自动释放
try-catch 实战
try
{
string input = Console.ReadLine() ?? "";
int age = int.Parse(input);
if (age < 0 || age > 150)
throw new ArgumentOutOfRangeException(nameof(age), "年龄超出范围");
Console.WriteLine($"明年 {age + 1} 岁");
}
catch (FormatException)
{
Console.WriteLine("请输入数字");
}
catch (ArgumentOutOfRangeException ex)
{
Console.WriteLine(ex.Message);
}
catch (Exception ex)
{
Console.WriteLine($"系统错误: {ex.Message}");
// 记录日志...
}
C# 文件的输入与输出
C# 文件 IO:读写文本、读写二进制、目录操作。
读取文件
using System.IO;
// 简单方式
string content = File.ReadAllText("data.txt");
string[] lines = File.ReadAllLines("data.txt");
byte[] bytes = File.ReadAllBytes("image.png");
// 流式读取(大文件)
using var reader = new StreamReader("big.txt");
string? line;
while ((line = reader.ReadLine()) != null)
{
Console.WriteLine(line);
}
写入文件
// 简单方式
File.WriteAllText("out.txt", "hello");
File.AppendAllText("log.txt", $"{DateTime.Now}\n"); // 追加
// 流式写入
using var writer = new StreamWriter("report.txt");
writer.WriteLine("标题: 销售报告");
writer.WriteLine($"日期: {DateTime.Now:yyyy-MM-dd}");
// 编码指定
File.WriteAllText("utf8.txt", "中文内容",
System.Text.Encoding.UTF8);
目录与文件操作
// 判断存在
File.Exists("a.txt");
Directory.Exists("folder");
// 目录操作
Directory.CreateDirectory("uploads/2026");
string[] files = Directory.GetFiles("folder"); // 所有文件
string[] txts = Directory.GetFiles("folder", "*.txt"); // 过滤
string[] dirs = Directory.GetDirectories("folder"); // 子目录
// 文件操作
File.Copy("a.txt", "b.txt", overwrite: true);
File.Move("a.txt", "archive/a.txt");
File.Delete("temp.txt");
// 信息
var info = new FileInfo("a.txt");
info.Length; // 大小
info.LastWriteTime; // 修改时间
info.Extension; // 扩展名
路径处理
string dir = @"C:\data";
string file = "report.txt";
// 拼接路径(自动处理分隔符)
string full = Path.Combine(dir, file);
Path.GetFileName(full); // "report.txt"
Path.GetExtension(full); // ".txt"
Path.GetDirectoryName(full); // "C:\data"
Path.GetFileNameWithoutExtension(full); // "report"
// 应用目录
string appDir = AppDomain.CurrentDomain.BaseDirectory;
string tempDir = Path.GetTempPath();
二进制文件
// 写入二进制
using var bw = new BinaryWriter(File.OpenWrite("data.bin"));
bw.Write(42); // int
bw.Write(3.14); // double
bw.Write("hello"); // 字符串
// 读取二进制
using var br = new BinaryReader(File.OpenRead("data.bin"));
int i = br.ReadInt32();
double d = br.ReadDouble();
string s = br.ReadString();
using 管理文件句柄;路径用 Path.Combine;处理用户输入路径时注意路径穿越安全(防 ../ 攻击)。C# 特性(Attribute)
特性(Attribute):给代码元素(类、方法、属性)附加元数据,运行时可通过反射读取。像「标签」一样描述代码。
内置特性
[Obsolete("用 NewMethod 代替")] // 标记废弃
public void OldMethod() { }
[Serializable] // 可序列化标记
public class Data { }
[Conditional("DEBUG")] // 条件编译
public void DebugLog() { }
[Flags] // 位标志枚举
[Enum]
自定义特性
// 定义特性类: 继承 Attribute
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,
AllowMultiple = false)]
public class AuthorAttribute : Attribute
{
public string Name { get; }
public string? Email { get; set; }
public AuthorAttribute(string name)
{
Name = name;
}
}
// 使用特性
[Author("张三", Email = "zhangsan@example.com")]
public class OrderService
{
[Author("李四")]
public void CreateOrder() { }
}
读取特性(反射)
// 读取类上的特性
var attrs = typeof(OrderService)
.GetCustomAttributes(typeof(AuthorAttribute), false);
foreach (AuthorAttribute a in attrs)
{
Console.WriteLine($"作者: {a.Name}, 邮箱: {a.Email}");
}
// 读取方法上的特性
var method = typeof(OrderService).GetMethod("CreateOrder")!;
var ma = method.GetCustomAttribute();
Console.WriteLine(ma?.Name); // 李四
特性应用场景
// 1. 数据校验(ASP.NET)
public class LoginModel
{
[Required(ErrorMessage = "用户名必填")]
[StringLength(20, MinimumLength = 3)]
public string Username { get; set; } = "";
[EmailAddress]
public string? Email { get; set; }
}
// 2. 序列化控制
[JsonIgnore]
public string PasswordHash { get; set; } = ""; // JSON 忽略
[JsonPropertyName("display_name")]
public string DisplayName { get; set; } = ""; // 改名
// 3. 路由/API 元数据(Web)
[HttpGet("api/orders/{id}")]
public IActionResult GetOrder(int id) { ... }
C# 反射(Reflection)
反射(Reflection):运行时检查并操作类型——获取类型信息、创建实例、调用方法、读写字段。是特性、序列化、依赖注入的底层机制。
获取类型
// 三种方式
Type t1 = typeof(DateTime);
Type t2 = new DateTime().GetType();
Type t3 = Type.GetType("System.DateTime"); // 按名称
// 类型信息
t1.Name; // "DateTime"
t1.FullName; // "System.DateTime"
t1.IsClass; // true
t1.IsValueType; // true(DateTime 是结构体)
t1.BaseType; // System.ValueType
遍历成员
Type t = typeof(string);
foreach (var m in t.GetMethods())
{
if (m.IsPublic && m.DeclaringType == t)
{
Console.WriteLine(m.Name);
}
}
// 获取属性
foreach (var p in t.GetProperties())
{
Console.WriteLine($"{p.Name}: {p.PropertyType.Name}");
}
动态创建与调用
// 创建实例
object obj = Activator.CreateInstance(typeof(List))!;
// 泛型类型创建
Type genType = typeof(List<>).MakeGenericType(typeof(string));
object list = Activator.CreateInstance(genType)!;
// 调用方法
Type t = typeof(Math);
MethodInfo? method = t.GetMethod("Pow");
object? result = method?.Invoke(null, new object[] { 2.0, 10.0 });
Console.WriteLine(result); // 1024
// 反射调用实例方法
var sb = new StringBuilder();
Type sbType = sb.GetType();
sbType.GetMethod("Append")!
.Invoke(sb, new object[] { "Hello" });
Console.WriteLine(sb); // Hello
读取字段/属性(通用打印器)
public static string Dump(object obj)
{
var sb = new StringBuilder();
Type t = obj.GetType();
sb.AppendLine($"{t.Name}:");
foreach (var p in t.GetProperties())
{
var val = p.GetValue(obj);
sb.AppendLine($" {p.Name} = {val}");
}
return sb.ToString();
}
Console.WriteLine(Dump(new { Name = "张三", Age = 25 }));
反射的代价
优点: 动态、通用、插件化(ORM/框架的基础)
缺点: 性能低(慢于直接调用)、可读性差、易出错
建议: 框架代码用, 业务代码少用;
高频路径用表达式树/生成器代替反射
C# 属性(Property)
属性(Property):类中对字段的安全访问接口——get/set 访问器,可加逻辑与校验。
基本属性
public class Employee
{
private string _name = "";
// 完整属性
public string Name
{
get { return _name; }
set { _name = value; } // value 是隐式参数
}
// 自动属性(无逻辑时)
public int Age { get; set; }
// 只读自动属性(构造赋值)
public string Id { get; } = Guid.NewGuid().ToString();
// 私有 set
public DateTime CreatedAt { get; private set; }
// 计算属性(只有 get)
public string Display => $"{Name} ({Age}岁)";
}
带校验的属性
private int _age;
public int Age
{
get => _age;
set
{
if (value < 0 || value > 150)
throw new ArgumentOutOfRangeException(nameof(Age));
_age = value;
}
}
// 或静默修正
public string Name
{
get => _name;
set => _name = string.IsNullOrWhiteSpace(value) ? "未命名" : value.Trim();
}
表达式体属性
public class Order
{
public List- Items { get; set; } = new();
// 计算属性(简洁)
public decimal Total => Items.Sum(i => i.Price * i.Quantity);
public bool IsEmpty => Items.Count == 0;
}
init 属性(C# 9+)
public class Product
{
public string Name { get; init; } = ""; // 只能在初始化时设置
public decimal Price { get; init; }
}
var p = new Product { Name = "手机", Price = 1999 }; // ✅
// p.Name = "其他"; // ❌ init 后不可改
// 配合 with 表达式做不可变更新
var p2 = p with { Price = 1499 };
属性 vs 字段
| 维度 | 字段 | 属性 |
|---|---|---|
| 访问控制 | 通常 private | 公开接口 |
| 可加逻辑 | 不能 | get/set 可加逻辑 |
| 校验 | 不能 | 可以 |
| 序列化 | 默认不序列化 | 默认序列化 |
C# 索引器(Indexer)
索引器(Indexer):让对象可以像数组一样用 [] 访问,本质是带参数的属性。
基本索引器
public class WordList
{
private readonly List _words = new();
public void Add(string word) => _words.Add(word);
// 索引器: this[int index]
public string this[int index]
{
get => _words[index];
set => _words[index] = value;
}
public int Count => _words.Count;
}
var words = new WordList();
words.Add("apple");
words.Add("banana");
Console.WriteLine(words[0]); // apple
words[1] = "orange"; // 修改
Console.WriteLine(words[1]); // orange
字符串键索引器(字典风格)
public class ConfigStore
{
private readonly Dictionary _data = new();
public string this[string key]
{
get => _data.TryGetValue(key, out var v) ? v : "";
set => _data[key] = value;
}
}
var config = new ConfigStore();
config["SiteName"] = "我的网站";
config["PageSize"] = "20";
Console.WriteLine(config["SiteName"]); // 我的网站
Console.WriteLine(config["PageSize"]); // 20
多维/多参索引器
public class Matrix
{
private readonly int[,] _data;
public Matrix(int rows, int cols)
{
_data = new int[rows, cols];
}
public int this[int row, int col]
{
get => _data[row, col];
set => _data[row, col] = value;
}
}
var m = new Matrix(2, 2);
m[0, 0] = 1;
m[1, 1] = 9;
Console.WriteLine(m[1, 1]); // 9
索引器与 foreach
// 实现 IEnumerable 后可用 foreach
using System.Collections;
public class NumberList : IEnumerable
{
private readonly int[] _nums;
public NumberList(int[] nums) => _nums = nums;
public int this[int i] => _nums[i];
public IEnumerator GetEnumerator() => _nums.GetEnumerator();
}
var list = new NumberList(new[] { 10, 20, 30 });
foreach (int n in list) { Console.WriteLine(n); }
常见用法
// 内置索引器示例
string s = "hello";
char c = s[1]; // 'e'
List nums = new() { 1, 2, 3 };
int v = nums[2]; // 3
// Span/数组等也支持索引
int[] arr = { 5, 6, 7 };
arr[^1]; // 7 (C# 8+ 从后索引)
C# 委托(Delegate)
委托(delegate):C# 中把方法当作参数/变量传递的类型——「方法的类型」。是事件、回调、LINQ 的基础。
委托基础
// 定义委托: 声明"方法的签名"
public delegate int MathOperation(int a, int b);
// 方法
static int Add(int a, int b) => a + b;
static int Multiply(int a, int b) => a * b;
// 委托变量指向方法
MathOperation op = Add;
int r1 = op(3, 4); // 7
op = Multiply; // 换方法
int r2 = op(3, 4); // 12
// 方法作为参数(回调)
static void Execute(int a, int b, MathOperation op)
{
Console.WriteLine(op(a, b));
}
Execute(5, 3, Add); // 8
内置委托 Func / Action / Predicate
// Func: 有返回值(最后一个是返回类型)
Func add = (a, b) => a + b;
Func square = x => x * x;
// Action: 无返回值
Action print = msg => Console.WriteLine(msg);
Action log = () => Console.WriteLine("记录日志");
// Predicate: 返回 bool
Predicate isEven = n => n % 2 == 0;
// 使用
print("Hello"); // Hello
Console.WriteLine(square(5)); // 25
多播委托(+=)
Action notify = () => Console.WriteLine("发送短信");
notify += () => Console.WriteLine("发送邮件");
notify += () => Console.WriteLine("推送通知");
notify();
// 输出:
// 发送短信
// 发送邮件
// 推送通知
// 移除
notify -= () => Console.WriteLine("发送邮件");
委托实战:排序比较器
List nums = new() { 3, 1, 4, 1, 5 };
// 自定义比较
nums.Sort((a, b) => b.CompareTo(a)); // 降序
// [5,4,3,1,1]
// 按条件筛选
var evens = nums.Where(n => n % 2 == 0); // LINQ 内部用委托
// 延迟执行示例
var sorted = nums.OrderBy(n => n); // n => n 是委托
lambda 与匿名方法
// Lambda 表达式
Func doubleIt = x => x * 2;
// 多参数 lambda
Func sum = (a, b) => a + b;
// 语句体 lambda
Action greet = name =>
{
string msg = $"你好, {name}";
Console.WriteLine(msg);
};
// 匿名方法(老语法)
Func old = delegate (int x) { return x * 3; };
Func/Action 就够了;自定义 delegate 只在需要语义化命名时使用。C# 事件(Event)
事件(event):基于委托的发布-订阅机制——对象状态变化时通知订阅者。是 GUI 按钮点击、异步通知的基石。
事件定义与订阅
public class Alarm
{
// 定义事件: 基于委托
public event Action? Ring;
public void Trigger()
{
Console.WriteLine("警报响起!");
Ring?.Invoke(); // 通知所有订阅者
}
}
// 使用
var alarm = new Alarm();
// 订阅事件
alarm.Ring += () => Console.WriteLine("发送短信提醒");
alarm.Ring += () => Console.WriteLine("拨打紧急电话");
alarm.Trigger();
// 输出:
// 警报响起!
// 发送短信提醒
// 拨打紧急电话
带参数事件(EventArgs)
// 事件参数
public class OrderEventArgs : EventArgs
{
public string OrderId { get; }
public decimal Amount { get; }
public OrderEventArgs(string id, decimal amount)
{
OrderId = id; Amount = amount;
}
}
public class OrderService
{
// 标准事件模式: EventHandler
public event EventHandler? OrderCreated;
public void Create(string id, decimal amount)
{
// 业务逻辑...
OrderCreated?.Invoke(this, new OrderEventArgs(id, amount));
}
}
// 订阅
var service = new OrderService();
service.OrderCreated += (sender, e) =>
Console.WriteLine($"新订单 {e.OrderId}, 金额 {e.Amount}");
service.OrderCreated += (sender, e) => SendReceipt(e.OrderId);
service.Create("A001", 199.5m);
内置事件:按钮点击(模拟)
public class Button
{
public event EventHandler? Click;
public void OnClick()
{
Click?.Invoke(this, EventArgs.Empty);
}
}
var btn = new Button();
btn.Click += (s, e) => Console.WriteLine("按钮被点击了");
btn.Click += (s, e) => SaveData();
btn.OnClick();
事件的限制(vs 委托)
// 事件只能在本类内部触发(Invoke)
// 外部只能 += / -=(订阅/退订)
// 委托可以直接调用与赋值
// 错误: 外部不能触发
// alarm.Ring?.Invoke(); // ❌ 编译错误(类外部)
// alarm.Ring = null; // ❌ 编译错误
// 正确: 外部只能订阅
alarm.Ring += Handler;
事件常见模式
// 属性变化通知
public class Person : INotifyPropertyChanged
{
private string _name = "";
public event PropertyChangedEventHandler? PropertyChanged;
public string Name
{
get => _name;
set
{
if (_name != value)
{
_name = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(Name)));
}
}
}
}
C# 集合(Collection)
C# 集合体系:List、Dictionary、HashSet、Queue、Stack——处理动态数据的主力。
List<T>(动态数组)
var list = new List();
list.Add(10); // 添加
list.AddRange(new[] { 20, 30 });
list.Insert(1, 15); // 插入到位置1
list.Remove(20); // 按值移除
list.RemoveAt(0); // 按下标移除
list.Contains(30); // true
list.Count; // 元素个数
list[0]; // 按下标访问
// 遍历
foreach (var n in list) { }
for (int i = 0; i < list.Count; i++) { }
// LINQ
list.Where(n => n > 10);
list.OrderBy(n => n);
list.Sum();
Dictionary<K,V>(键值对)
var dict = new Dictionary();
dict["apple"] = 5;
dict["banana"] = 3;
// 安全读取
if (dict.TryGetValue("apple", out int count))
{
Console.WriteLine(count); // 5
}
dict.ContainsKey("orange"); // false
dict.Remove("banana");
dict.Count;
// 遍历
foreach (var kv in dict)
{
Console.WriteLine($"{kv.Key}: {kv.Value}");
}
// 不存在时给默认值
dict.GetValueOrDefault("grape"); // 0
HashSet<T>(唯一集合)
var set = new HashSet();
set.Add(1);
set.Add(1); // 忽略重复
set.Add(2);
set.Count; // 2
set.Contains(1); // true
// 集合运算
var a = new HashSet { 1, 2, 3 };
var b = new HashSet { 2, 3, 4 };
a.UnionWith(b); // {1,2,3,4} 并
a.IntersectWith(b); // 交
a.ExceptWith(b); // 差
// 去重
var unique = new HashSet(new[] { 1, 1, 2, 3, 3 }); // {1,2,3}
Queue / Stack
// 队列 FIFO
var queue = new Queue();
queue.Enqueue("任务1");
queue.Enqueue("任务2");
queue.Dequeue(); // "任务1"
queue.Peek(); // "任务2" (不取出)
// 栈 LIFO
var stack = new Stack();
stack.Push(1);
stack.Push(2);
stack.Pop(); // 2
stack.Peek(); // 1
集合选型
| 集合 | 特性 | 场景 |
|---|---|---|
List<T> | 有序、可重复、索引访问 | 默认首选 |
Dictionary<K,V> | 键值、O(1) 查找 | 按键查数据 |
HashSet<T> | 唯一、O(1) 查找 | 去重、集合运算 |
Queue<T> | FIFO | 任务队列、BFS |
Stack<T> | LIFO | 撤销、括号匹配 |
集合初始化与不可变
// 初始化语法
var nums = new List { 1, 2, 3 };
var map = new Dictionary
{
["a"] = 1,
["b"] = 2
};
// 不可变(线程安全/共享)
using System.Collections.Immutable;
var frozen = nums.ToImmutableList();
List<T> + Dictionary<K,V> 就够了;需要去重用 HashSet;注意 Dictionary 的键必须唯一且不可变。C# 泛型(Generic)
泛型(Generic):编写类型无关的代码——「用类型占位符 T 定义,使用时指定具体类型」。是集合、LINQ、异步的基础。
为什么需要泛型
// 非泛型: 每种类型写一遍(或装箱损失性能)
class IntBox { public int Value { get; set; } }
class StringBox { public string? Value { get; set; } }
// 泛型: 一份代码通用
class Box
{
public T? Value { get; set; }
}
var intBox = new Box { Value = 42 };
var strBox = new Box { Value = "hello" };
泛型方法
// 泛型方法: 交换两个值
public static void Swap(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
int x = 1, y = 2;
Swap(ref x, ref y); // T 推断为 int
string s1 = "a", s2 = "b";
Swap(ref s1, ref s2); // T 推断为 string
// 泛型打印
public static void PrintAll(IEnumerable items)
{
foreach (var item in items)
Console.WriteLine(item);
}
泛型约束
// where T : 约束
public class Repository where T : class // 引用类型
{ }
public class ValueRepo where T : struct // 值类型
{ }
// 接口约束 + new()
public class Factory where T : IComparable, new()
{
public T Create() => new T();
}
// 多约束
public void Process(T item)
where T : class, IDisposable, new()
{
using var obj = new T();
}
// 数值运算(现代 C# 支持静态抽象成员)
public static T Add(T a, T b) where T : INumber
=> a + b;
Console.WriteLine(Add(3, 4)); // 7
Console.WriteLine(Add(1.5, 2.5)); // 4.0
泛型类实战
public class Stack
{
private readonly List _items = new();
public void Push(T item) => _items.Add(item);
public T Pop()
{
T item = _items[^1];
_items.RemoveAt(_items.Count - 1);
return item;
}
public T Peek() => _items[^1];
public int Count => _items.Count;
}
var stack = new Stack();
stack.Push(10); stack.Push(20);
Console.WriteLine(stack.Pop()); // 20
泛型与性能
// 泛型避免装箱: List 直接存值
var list = new List(); // ✅ 无装箱
// 非泛型 ArrayList 会装箱
System.Collections.ArrayList old = new();
old.Add(42); // int → object 装箱 ❌
// 泛型是编译期生成具体类型 → 类型安全 + 零开销
C# 匿名方法
匿名方法 / Lambda:不声明名字的方法,内联定义,配合委托使用。
Lambda 基础
// Lambda: 参数 => 表达式
Func square = x => x * x;
Func add = (a, b) => a + b;
// 无参
Action hello = () => Console.WriteLine("Hello");
// 语句体(多行)
Action show = name =>
{
Console.WriteLine($"开始处理 {name}");
Console.WriteLine("完成");
};
// 使用
Console.WriteLine(square(5)); // 25
Console.WriteLine(add(2, 3)); // 5
show("订单");
匿名方法(旧语法)
// delegate 关键字匿名方法
Func doubleIt = delegate (int x)
{
return x * 2;
};
Console.WriteLine(doubleIt(4)); // 8
闭包(捕获外部变量)
int factor = 3;
// Lambda 捕获外部变量 factor
Func multiply = x => x * factor;
Console.WriteLine(multiply(10)); // 30
factor = 5; // 捕获的是引用!
Console.WriteLine(multiply(10)); // 50 (值已变)
Lambda 在 LINQ 中的应用
var numbers = new List { 1, 2, 3, 4, 5, 6 };
// 筛选
var evens = numbers.Where(n => n % 2 == 0); // 2,4,6
// 映射
var squared = numbers.Select(n => n * n); // 1,4,9,...
// 排序
var desc = numbers.OrderByDescending(n => n);
// 聚合
int sum = numbers.Aggregate((a, b) => a + b); // 21
// 分组
var groups = numbers.GroupBy(n => n % 3); // 按余数分组
// 是否存在
bool any = numbers.Any(n => n > 5); // true
Lambda 与排序
var people = new List<(string Name, int Age)>
{
("张三", 30),
("李四", 22),
("王五", 35)
};
// 按年龄升序
var byAge = people.OrderBy(p => p.Age);
// 自定义比较
people.Sort((a, b) => a.Age.CompareTo(b.Age));
// 多级排序
var multi = people
.OrderByDescending(p => p.Age)
.ThenBy(p => p.Name);
C# 不安全代码
不安全代码(unsafe):绕过托管环境直接操作指针,用于高性能场景或互操作。默认不推荐,需显式开启。
启用 unsafe
<!-- csproj 中 -->
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
基本指针操作
unsafe
{
int value = 42;
int* p = &value; // 取地址
Console.WriteLine(*p); // 42 解引用
*p = 100; // 通过指针修改
Console.WriteLine(value); // 100
}
fixed 固定内存
unsafe
{
int[] arr = { 10, 20, 30, 40 };
// fixed: 防止 GC 移动数组, 才能取指针
fixed (int* p = arr)
{
for (int i = 0; i < arr.Length; i++)
{
Console.Write(*(p + i) + " "); // 指针算术
}
// 10 20 30 40
}
}
指针与数组
unsafe
{
int[] nums = { 1, 2, 3, 4, 5 };
fixed (int* start = nums)
{
int* p = start;
Console.WriteLine(*p); // 1
Console.WriteLine(*(p + 2)); // 3 (指针偏移)
Console.WriteLine(p[4]); // 5 (下标语法)
// 遍历
for (int i = 0; i < 5; i++)
Console.Write(*(start + i));
}
}
String 指针互操作
unsafe
{
string s = "hello";
fixed (char* p = s)
{
for (int i = 0; i < s.Length; i++)
Console.Write(*(p + i)); // h e l l o
}
}
为什么要用 unsafe
✅ 高性能场景(图像处理、数值计算、游戏引擎)
✅ 调用原生 C/C++ 库(互操作)
✅ 需要内存布局控制(序列化、协议)
❌ 增加风险: 指针错误 = 崩溃/内存损坏
❌ 失去 GC 保护, 需手动管理
❌ 不是所有平台支持(如浏览器 WASM 部分限制)
stackalloc(栈上分配)
unsafe
{
// 栈上分配数组(避免 GC)
Span buffer = stackalloc int[100];
for (int i = 0; i < 100; i++)
buffer[i] = i * 2;
Console.WriteLine(buffer[50]); // 100
}
// 现代 C# 用 Span 更安全, 不需要 unsafe
Span<T>、Marshal 等安全替代即可。仅在确需极致性能或互操作时使用,并做好边界检查。C# 多线程
C# 并发编程:Task / async-await / Parallel / lock——让程序同时做多件事。
Task 基础
// 创建任务
Task task = Task.Run(() =>
{
Thread.Sleep(1000);
Console.WriteLine("后台任务完成");
});
// 等待
task.Wait(); // 阻塞直到完成
// 有返回值
Task calc = Task.Run(() => 3 * 4);
int result = calc.Result; // 12
// 多个任务
Task t1 = Task.Run(Work1);
Task t2 = Task.Run(Work2);
Task.WaitAll(t1, t2); // 全部完成
async / await(推荐)
using System.Net.Http;
public static async Task Main()
{
await RunAsync();
}
static async Task RunAsync()
{
Console.WriteLine("开始下载");
using var client = new HttpClient();
// await: 不阻塞线程, 完成后继续
string html = await client.GetStringAsync(
"https://example.com");
Console.WriteLine($"下载完成, {html.Length} 字符");
}
// async 方法规范:
// - 返回 Task(无值) / Task(有值)
// - 内部必须有 await(否则警告)
// - 命名以 Async 结尾
Parallel(数据并行)
// 并行处理集合
Parallel.For(0, 100, i =>
{
Console.WriteLine($"处理 {i}");
});
// 并行 LINQ
var results = Enumerable.Range(1, 1000)
.AsParallel()
.Where(n => n % 2 == 0)
.Select(n => n * n)
.ToList();
// 控制并发度
var options = new ParallelOptions { MaxDegreeOfParallelism = 4 };
Parallel.For(0, 100, options, i => Work(i));
线程安全与锁
class Counter
{
private int _count;
private readonly object _lock = new();
public void Increment()
{
lock (_lock) // 互斥锁
{
_count++;
}
}
public int Value => _count;
}
// 并发安全集合
using System.Collections.Concurrent;
var dict = new ConcurrentDictionary();
var bag = new ConcurrentBag();
// Interlocked(原子操作)
Interlocked.Increment(ref _count);
Thread 直接使用(低层)
Thread t = new(() =>
{
for (int i = 0; i < 5; i++)
Console.WriteLine($"线程: {i}");
});
t.IsBackground = true; // 后台线程
t.Start();
t.Join(); // 等待
Thread.Sleep(100); // 当前线程休眠
选型建议
| 场景 | 方案 |
|---|---|
| IO 操作(网络/文件) | async/await ✅ |
| CPU 密集计算 | Parallel / Task.Run |
| 共享数据修改 | lock / Concurrent 集合 |
| 定时/后台任务 | Timer / BackgroundService |
async/await(不占线程);CPU 密集用 Parallel;共享状态要加锁;优先无共享的「数据并行」。C# 语言测验
通过自测题检验本教程学习成果,覆盖基础语法、面向对象、高级特性。
一、选择题
1. 下列哪个是 C# 入口方法的正确签名?
A. static void Main() ✅
B. public Main()
C. static Main(int)
D. void main()
2. 值类型与引用类型的区别?
A. 值类型存值, 引用类型存地址 ✅
3. string 是否可变?
A. 可变 B. 不可变(每次生成新对象) ✅
4. 接口与抽象类的区别?
A. 接口纯契约可多实现; 抽象类可有实现单继承 ✅
5. 委托的用途?
A. 把方法当参数传递 ✅
6. List 与数组的区别?
A. List 长度可变, 数组固定 ✅
7. async/await 的好处?
A. IO 操作不阻塞线程 ✅
8. 哪个集合去重且 O(1) 查找?
A. HashSet ✅
二、补全代码
// 1. 声明一个只读属性
public string Name { ______; } // get;
// 2. 泛型方法: 交换
public static void Swap(ref T a, ref T b) { ______ }
// 3. Lambda 筛选偶数
var evens = nums.______(n => n % 2 == 0); // Where
// 4. 事件订阅
btn.Click ______ (s, e) => Save(); // +=
// 5. 空值兜底
string display = name ______ "默认"; // ??
三、判断题
1. C# 区分大小写 ✅ 对
2. struct 是引用类型 ❌ 错(值类型)
3. 接口可以包含字段 ❌ 错(不能)
4. const 字段编译期确定 ✅ 对
5. 泛型可以避免装箱 ✅ 对
6. 事件外部可以直接触发 Invoke ❌ 错
7. foreach 中可以修改集合 ❌ 错
8. ref 参数可以修改原变量 ✅ 对
9. lambda 捕获外部变量按引用 ✅ 对
10. unsafe 代码默认允许 ❌ 错(需开启)
四、编程题
// 1. 实现一个简单 BankAccount: 存款/取款/余额(带校验)
// 2. 用 async/await 模拟一次网络请求
// 3. 用 LINQ 从集合筛选 + 排序 + 求和
// 4. 定义接口 IShape 并实现 Circle/Rectangle
// 5. 用 event 实现"订单完成通知"
// 参考答案(思路):
// 1. private decimal _balance + 方法校验
// 2. await Task.Delay(1000) 模拟
// 3. list.Where(...).OrderBy(...).Sum()
// 4. interface IShape { double Area(); }
// 5. event EventHandler Completed
成绩对照
| 得分 | 评价 |
|---|---|
| 90-100 | 🎉 掌握扎实,可以实战项目了 |
| 70-89 | 👍 基础良好,复习薄弱章节 |
| 50-69 | 📖 建议回看对应章节 |
| <50 | 🔁 重新过一遍教程,动手敲代码 |