一.c# 初级
一.c# 基础阶段
1.程序结构
using System; // 引入命名空间
namespace HelloWorld // 命名空间声明
{
class Program // 类声明
{
static void Main(string[] args) // Main方法,程序入口点
{
Console.WriteLine("Hello, World!"); // 语句
}
}
}
2.关键字
C# 关键字是语言中具有特殊含义的保留字,不能用作标识符(如变量名、类名等) 1). 访问修饰符
| 关键字 | 描述 |
|---|---|
| public | 访问不受限制 |
| private | 只能在当前类中访问 |
| protected | 只能在当前类或派生类中访问 |
| internal | 只能在当前程序集中访问 |
| protected internal | 在当前程序集或派生类中可访问 |
| private protected | 在当前程序集的派生类中可访问 |
| file | 仅在当前文件中可访问 |
2). 类型定义
| 关键字 | 描述 |
|---|---|
| class | 定义类 |
| struct | 定义结构 |
| interface | 定义接口 |
| enum | 定义枚举 |
| delegate | 定义委托 |
| record | 定义记录类型(新特性) |
3). 基本数据类型
| 关键字 | 描述 |
|---|---|
| bool | 布尔值(true/false) |
| byte, sbyte | 8位整数 |
| short, ushort | 16位整数 |
| int, uint | 32位整数 |
| long, ulong | 64位整数 |
| float | 32位浮点数 |
| double | 64位浮点数 |
| decimal | 128位十进制数 |
| char | Unicode字符 |
| string | 字符串 |
| object | 所有类型的基类 |
| dynamic | 动态类型 |
4).流程控制
| 关键字 | 描述 |
|---|---|
| if, else | 条件语句 |
| switch, case, default | 多分支选择 |
| for, foreach | 循环结构 |
| while, do | 循环结构 |
| break | 跳出循环或switch |
| continue | 跳过当前循环迭代 |
| goto | 跳转到标签(不推荐) |
| return | 从方法返回值 |
5).方法相关
| 关键字 | 描述 |
|---|---|
| void | 表示方法不返回值 |
| ref | 按引用传递参数 |
| out | 输出参数 |
| params | 可变数量参数 |
| async | 异步方法 |
| await | 等待异步操作完成 |
6).类成员修饰符
| 关键字 | 描述 |
|---|---|
| static | 静态成员 |
| const | 常量 |
| readonly | 只读字段 |
| new | 创建对象或隐藏基类成员 |
| this | 当前实例引用 |
| base | 基类访问 |
| virtual | 可重写的方法 |
| override | 重写基类方法 |
| abstract | 抽象类或方法 |
| sealed | 密封类或方法 |
| partial | 分部类/方法 |
7).异常处理
| 关键字 | 描述 |
|---|---|
| try | 尝试执行代码块 |
| catch | 捕获异常 |
| finally | 无论是否异常都会执行 |
| throw | 抛出异常 |
8).命名空间和程序集
| 关键字 | 描述 |
|---|---|
| namespace | 定义命名空间 |
| using | 引入命名空间或创建别名 |
9).泛型
| 关键字 | 描述 |
|---|---|
| where | 泛型类型约束 |
| default | 获取类型的默认值 |
10).类型操作
| 关键字 | 描述 |
|---|---|
| is | 类型检查 |
| as | 安全类型转换 |
| sizeof | 获取类型大小 |
| typeof | 获取类型信息 |
| nameof | 获取变量、类型或成员的名称 |
11).上下文关键字
| 关键字 | 描述 |
|---|---|
| add, remove | 事件访问器 |
| get, set | 属性访问器 |
| init | 仅初始化属性设置器 |
| value | 属性设置器中的隐式参数 |
| global | 全局命名空间别名 |
| when | catch或switch case的筛选条件 |
| with | 用于记录类型的非破坏性修改 |
12).不安全代码
| 关键字 | 描述 |
|---|---|
| unsafe | 不安全代码块 |
| fixed | 固定指针 |
| stackalloc | 栈上分配内存 |
| volatile | 易变字段 |
13).运算符重载
| 关键字 | 描述 |
|---|---|
| operator | 重载运算符 |
| checked, unchecked | 控制算术溢出检查 |
14).其他
| 关键字 | 描述 |
|---|---|
| var | 隐式类型变量 |
| yield | 迭代器生成 |
3.数据类型和转换
3.1 值类型
整数类型
| 类型 | 关键字 | 大小 | 取值范围/描述 | 默认值 | 示例 |
|---|---|---|---|---|---|
| 有符号字节 | sbyte | 8位 | -128 到 127 | 0 | sbyte a = -100; |
| 无符号字节 | byte | 8位 | 0 到 255 | 0 | byte b = 200; |
| 有符号短整型 | short | 16位 | -32,768 到 32,767 | 0 short c = -20000; | |
| 无符号短整型 | ushort | 16位 | 0 到 65,535 | 0 | ushort d = 60000; |
| 有符号整型 | int | 32位 | -2,147,483,648 到 2,147,483,647 | 0 | int e = -1000000; |
| 无符号整型 | uint | 32位 | 0 到 4,294,967,295 | 0 | uint f = 4000000; |
| 有符号长整型 | long | 64位 | -9.2×10¹⁸ 到 9.2×10¹⁸ | 0L | long g = -5L; |
| 无符号长整型 | ulong | 64位 | 0 到 1.8×10¹⁹ | 0UL | ulong h = 10UL; |
浮点类型
| 类型 | 关键字 | 大小 | 精度/范围 | 默认值 | 示例 |
|---|---|---|---|---|---|
| 单精度浮点 | float | 32位 | 7位精度,±1.5×10⁻⁴⁵到±3.4×10³⁸ | 0.0F | float i = 3.14F; |
| 双精度浮点 | double | 64位 | 15-16位精度,±5.0 × 10⁻³²⁴ 到 ±1.7 × 10³⁰⁸ | 0.0D | double j = 3.14159; |
| 十进制数 | decimal | 128位 | 28-29位精度,±1.0 × 10⁻²⁸ 到 7.9 × 10²⁸ | 0.0M | decimal k = 123.456M; |
其他值类型
| 类型 | 关键字 | 大小 | 描述 | 默认值 | 示例 |
|---|---|---|---|---|---|
| 布尔型 | bool | 8位 | true 或 false | false | bool m = true; |
| 字符型 | char | 16位 | Unicode字符 (U+0000到U+FFFF) | '\0' | char l = 'A'; |
复合值类型
// 结构体示例
public struct Point
{
public int X;
public int Y;
}
// 枚举示例
public enum Color : byte // 可指定基础类型
{
Red = 1,
Green = 2,
Blue = 3
}
// 元组示例
var tuple = (1, "text"); // ValueTuple<int, string>
3.2 引用类型
内置引用类型
| 类型 | 关键字 | 描述 | 默认值 | 示例 |
|---|---|---|---|---|
| 字符串 | string | Unicode字符序列 | null | string s = "Hello"; |
| 对象 | object | 所有类型的基类 | null | object o = new(); |
| 动态类型 | dynamic | 运行时类型检查 | null | dynamic d = 10; |
复合引用类型
// 类示例
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
// 接口示例
public interface IDrawable
{
void Draw();
}
// 数组示例
int[] numbers = { 1, 2, 3 }; // 一维数组
int[,] matrix = new int[2, 3]; // 二维数组
int[][] jagged = new int[3][]; // 交错数组
// 委托示例
public delegate void MyDelegate(string msg);
记录类型
// 记录类 (不可变引用类型)
public record Product(string Name, decimal Price);
// 记录结构 (不可变值类型)
public readonly record struct Point(int X, int Y);
3.3 特殊类型
可为空值类型
int? nullableInt = null; // Nullable<int>
double? nullableDouble = 3.14; // Nullable<double>
bool? nullableBool = null; // Nullable<bool>
// 安全访问
int value = nullableInt ?? -1; // 如果null则返回-1
指针类型
unsafe
{
int value = 10;
int* pointer = &value; // 获取地址
// 指针运算
int* p = stackalloc int[10]; // 栈上分配
p[0] = 1;
// 固定托管对象
int[] array = { 1, 2, 3 };
fixed (int* ptr = array)
{
for (int i = 0; i < array.Length; i++)
{
Console.WriteLine(*(ptr + i));
}
}
}
其他特殊类型
| 类型/概念 | 描述 | 示例 |
|---|---|---|
| void | 表示无返回值 | void Method() |
| var | 隐式类型推断 | var x = 10; |
| nint/nuint | 平台相关整数大小 | nint size = 100; |
3.4 类型转换
隐式转换 (自动)
int i = 10;
long l = i; // 小范围转大范围
显式转换 (强制)
double d = 3.14;
int i = (int)d; // 大范围转小范围
方法转换
// Parse/TryParse
int num = int.Parse("123");
bool success = int.TryParse("abc", out int result);
// Convert类
decimal dec = Convert.ToDecimal(3.14);
类型检查与转换
object obj = "Hello";
// is 检查
if (obj is string) { /*...*/ }
// as 安全转换
string str = obj as string;
if (str != null) { /*...*/ }
// 模式匹配 (C# 7+)
if (obj is string s) { /* 使用s */ }
4.变量
4.1 变量基础概念
变量定义
- 变量是存储数据的命名内存位置
- 必须先声明后使用
- 包含:变量名、数据类型和值
变量声明语法
数据类型 变量名; // 声明
数据类型 变量名 = 初始值; // 声明并初始化
4.2 变量类型分类
按数据类型分类
| 类型 | 示例 | 说明 |
|---|---|---|
| 值类型变量 | int age = 25; | 直接存储数据 |
| 引用类型变量 | string name = "Tom"; | 存储数据引用 |
| 指针类型变量 | int* ptr; | 存储内存地址(unsafe上下文) |
按作用域分类
| 类型 | 声明位置 | 生命周期 |
|---|---|---|
| 局部变量 | 方法/代码块内 | 所在代码块执行期间 |
| 字段变量 | 类/结构体内 | 所属对象存在期间 |
| 静态变量 | 类中用static声明 | 程序运行期间 |
| 参数变量 | 方法参数列表 | 方法执行期间 |
4.3 变量/常量声明
基本变量声明
int count; // 声明未初始化
double price = 19.99; // 声明并初始化
char grade = 'A'; // 字符类型
bool isCompleted = false; // 布尔类型
类型推断(var)
var message = "Hello"; // 编译为string
var number = 42; // 编译为int
var list = new List<int>(); // 编译为List<int>
// var必须初始化,不能用于字段声明
// var不能用于方法参数和返回类型
常量声明
const double PI = 3.14159;
const int MaxUsers = 100;
// 常量必须在声明时初始化
// 常量默认为静态(static)
元组变量
// 命名元组
var person = (Name: "Alice", Age: 25);
Console.WriteLine(person.Name);
// 解构元组
(string name, int age) = person;
4.4 变量作用域规则
局部变量作用域
void Method()
{
int x = 10; // 作用域开始
if (true)
{
int y = 20; // 仅在此块内有效
x = 30; // 可以访问外部变量
}
// y = 40; // 错误!y不可访问
} // 作用域结束,x被销毁
字段变量作用域
class MyClass
{
private int _field; // 类级作用域
void Method()
{
_field = 10; // 可在类内任何方法访问
}
}
变量隐藏
int x = 10;
if (true)
{
int x = 20; // 隐藏外部x
Console.WriteLine(x); // 20
}
Console.WriteLine(x); // 10
4.5 特殊变量类型
可为空变量
int? nullableInt = null; // Nullable<int>
double? price = null;
// 安全访问
int value = nullableInt ?? -1; // null合并运算符
固定大小缓冲区
unsafe struct FixedBuffer
{
public fixed char buffer[128]; // 固定大小数组
}
范围变量
范围变量(Range Variable)是LINQ查询表达式中的特殊变量,它表示数据源中的单个元素。范围变量是LINQ查询中的"临时代表",代表数据集合中的单个元素,只在当前查询表达式中有效,类似于foreach循环中的迭代变量。
var query = from num in numbers
where num > 5
select num; // num是范围变量
4.6 变量命名规范
- 常规变量:camelCase (totalCount, userName)
- 私有字段:_camelCase (_instanceCount, _isInitialized)
- 常量:PascalCase (MAX_SIZE, DEFAULT_TIMEOUT)
- 布尔变量:以is/can/has开头 (isActive, hasPermission)
5.运算符和表达式
5.1 运算符
算术运算符
| 运算符 | 描述 | 示例 | 结果 |
|---|---|---|---|
| + | 加法 | 5 + 3 | 8 |
| - | 减法 | 10 - 4 | 6 |
| _ | 乘法 | 3 _ 4 | 12 |
| / | 除法 | 10 / 3 | 3 |
| % | 取模 | 10 % 3 | 1 |
| ++ | 自增 | a++ | a+1 |
| -- | 自减 | a-- | a-1 |
关系运算符
| 运算符 | 描述 | 示例 | 结果 |
|---|---|---|---|
| == | 等于 | 5 == 5 | true |
| != | 不等于 | 5 != 3 | true |
| > | 大于 | 5 > 3 | true |
| < | 小于 | 5 < 3 | false |
| >= | 大于等于 | 5 >= 5 | true |
| <= | 小于等于 | 5 <= 3 | false |
逻辑运算符
位运算符

赋值运算符
| 运算符 | 示例 | 等价形式 | |
|---|---|---|---|
| = | a = 5 | | |
| += | a += 3 | a = a + 3 | |
| -= | a -= 2 | a = a - 2 | |
| * = | a *= 4 | a = a * 4 | |
| /= | a /= 2 | a = a / 2 | |
| %= | a %= 3 | a = a % 3 |
特殊运算符
| 运算符 | 描述 | 示例 |
|---|---|---|
| ?: | 三元条件运算符 | x > 0 ? 1 : -1 |
| ?? | null合并运算符 | name ?? "匿名" |
| ?. | null条件运算符 | person?.Name |
| is | 类型检查 | obj is string |
| as | 安全类型转换 | obj as string |
| sizeof | 获取类型大小 | sizeof(int) |
| typeof | 获取类型对象 | typeof(string) |
5.2 表达式
基本表达式
42 // 字面量表达式
a // 变量表达式
a + b // 算术表达式
a > b // 关系表达式
复合表达式
(a + b) * c // 组合算术表达式
(a > b) && (c < d) // 组合逻辑表达式
Lambda表达式
x => x * x // 单参数Lambda
() => Console.WriteLine("Hello") // 无参数Lambda
查询表达式
from n in numbers
where n > 5
select n // LINQ查询表达式
5.3 运算符优先级
(从高到低)1.成员访问 .、方法调用 ()、数组索引 [] 2.一元运算符 +、-、!、~、++、--、(类型) 3.乘除 *、/、% 4.加减 +、- 5.移位 <<、>> 6.关系 <、>、<=、>=、is、as 7.相等 ==、!= 8.位与 & 9.位异或 ^ 10.位或 | 11.逻辑与 && 12.逻辑或 || 13.null合并 ?? 14.三元条件 ?: 15.赋值 =、+=、-= 等
5.4 实用技巧
1.用括号明确优先级:(a + b) _ c 比 a + b _ c 更清晰
2.null检查简化:
// 传统方式
if (obj != null) { var name = obj.Name; }
// 使用?.运算符
var name = obj?.Name;
3.类型转换最佳实践:
// 安全转换(推荐)
string s = obj as string;
if (s != null) { ... }
// 强制转换(确定类型时使用)
string s = (string)obj;
4.模式匹配(C# 7.0+):
if (obj is string str) {
Console.WriteLine(str.Length);
}
5.范围运算符(C# 8.0+):
int[] arr = {1, 2, 3, 4, 5};
var sub = arr[1..4]; // 获取索引1-3 [2,3,4]
5.5 注意事项
1.整数除法会截断小数部分:5 / 2 = 2 2.++a(前缀)和 a++(后缀)有区别:
int a = 1;
int b = a++; // b=1, a=2
int c = ++a; // c=3, a=3
3.浮点数比较应使用容差而非直接==:
// 不推荐
if (d1 == d2) {...}
// 推荐
if (Math.Abs(d1 - d2) < 0.0001) {...}
4.运算符重载示例:
public class Program
{
public static void Main(string[] args)
{
Vector2D v1 = new Vector2D(1, 2);
Vector2D v2 = new Vector2D(3, 4);
var n = (v1 + v2);
Console.WriteLine($"new x:{n.X},new y:{n.Y}");
}
}
public class Vector2D
{
public double X { get; set; }
public double Y { get; set; }
public Vector2D(double x, double y)
{
X = x;
Y = y;
}
public static Vector2D operator +(Vector2D a, Vector2D b)
{
return new Vector2D(a.X + b.X, a.Y + b.Y);
}
public static Vector2D operator -(Vector2D a, Vector2D b)
{
return new Vector2D(a.X - b.X, a.Y - b.Y);
}
}
6.控制结构
6.1 条件控制结构
if
if (条件表达式)
{
// 条件为真时执行的代码
}
if (条件表达式)
{
// 条件为真时执行
}
else
{
// 条件为假时执行
}
if (条件1)
{
// 条件1为真时执行
}
else if (条件2)
{
// 条件2为真时执行
}
else
{
// 所有条件为假时执行
}
switch
switch (表达式)
{
case 值1:
// 代码块1
break;
case 值2:
// 代码块2
break;
default:
// 默认代码块
break;
}
// 7.0+ 增强特性
switch (shape)
{
case Circle c:
Console.WriteLine($"圆形,半径={c.Radius}");
break;
case Rectangle r when r.Width == r.Height:
Console.WriteLine($"正方形,边长={r.Width}");
break;
case null:
throw new ArgumentNullException(nameof(shape));
default:
Console.WriteLine("未知形状");
break;
}
6.2 循环控制
for
for (初始化; 条件; 迭代)
{
// 循环体
}
// 示例
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
}
while
while (条件表达式)
{
// 循环体
}
// 示例
int i = 0;
while (i < 10)
{
Console.WriteLine(i);
i++;
}
do-while
do
{
// 循环体(至少执行一次)
} while (条件表达式);
// 示例
int i = 0;
do
{
Console.WriteLine(i);
i++;
} while (i < 10);
foreach
foreach (类型 变量 in 集合)
{
// 循环体
}
// 示例
string[] names = { "Alice", "Bob", "Charlie" };
foreach (string name in names)
{
Console.WriteLine(name);
}
6.3 跳转语句
break
立即终止当前循环或switch语句
for (int i = 0; i < 10; i++)
{
if (i == 5)
break; // 当i=5时退出循环
Console.WriteLine(i);
}
continue 跳过当前循环迭代,继续下一次迭代
for (int i = 0; i < 10; i++)
{
if (i % 2 == 0)
continue; // 跳过偶数
Console.WriteLine(i);
}
return 立即退出当前方法,并可返回一个值
int Add(int a, int b)
{
return a + b; // 返回计算结果
}
goto
for (int i = 0; i < 10; i++)
{
if (i == 5)
goto EndLoop; // 跳转到标签处
Console.WriteLine(i);
}
EndLoop:
Console.WriteLine("循环结束");
6.4 特殊控制结构
try-catch-finally
try
{
// 可能抛出异常的代码
}
catch (特定异常类型 ex)
{
// 处理特定异常
}
catch (Exception ex)
{
// 处理所有其他异常
}
finally
{
// 无论是否发生异常都会执行的代码
}
using
using (var resource = new DisposableResource())
{
// 使用资源
// 离开作用域时自动调用Dispose(),在处理IO流相关操作时用到
}
yield return
IEnumerable<int> GetNumbers()
{
for (int i = 0; i < 10; i++)
{
yield return i; // 延迟生成值
}
}
6.5 C# 8.0+ 新特性
switch
var result = operation switch
{
"+" => a + b,
"-" => a - b,
"*" => a * b,
"/" => a / b,
_ => throw new InvalidOperationException("未知运算符")
};
模式匹配增强
if (obj is string { Length: >5 } s)
{
Console.WriteLine($"长字符串: {s}");
}
异步流
await foreach (var item in GetAsyncItems())
{
Console.WriteLine(item);
}
7.数组
7.1 数组基础概念
7.1.1 数组核心特性
- 固定大小:创建后长度不可变
- 类型统一:所有元素必须是相同类型
- 连续内存:元素在内存中顺序存储
- 索引访问:通过下标访问元素(从0开始)
- 性能优势:访问速度快(O(1)),缓存友好
7.1.2 数组分类
- 一维数组:int[]
- 多维数组:int[,](矩形数组)
- 锯齿数组:int[][](数组的数组)
7.2 数组声明与初始化
基本语法
// 方式1:声明后初始化
int[] numbers1;
numbers1 = new int[5]; // 5个元素,默认值0
// 方式2:声明时初始化
int[] numbers2 = new int[] {1, 2, 3};
// 方式3:简化初始化
int[] numbers3 = {1, 2, 3, 4, 5};
// 方式4:指定大小
int[] numbers4 = new int[5] {1, 2, 3, 4, 5};
多维数组
// 二维数组(矩形数组)
int[,] matrix = new int[3,4]; // 3行4列
// 初始化二维数组
int[,] matrix2 = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// 访问元素
int val = matrix2[1,2]; // 第2行第3列(6)
锯齿数组
// 声明锯齿数组
int[][] jagged = new int[3][];
// 初始化子数组
jagged[0] = new int[2] {1, 2};
jagged[1] = new int[3] {3, 4, 5};
jagged[2] = new int[4] {6, 7, 8, 9};
// 访问元素
int item = jagged[1][2]; // 5
7.3 数组操作
基本操作
int[] arr = {1, 2, 3, 4, 5};
// 访问元素
int first = arr[0]; // 1
// 修改元素
arr[1] = 10; // 数组变为[1,10,3,4,5]
// 获取长度
int len = arr.Length; // 5
// 遍历数组
for (int i = 0; i < arr.Length; i++) {
Console.WriteLine(arr[i]);
}
foreach (int num in arr) {
Console.WriteLine(num);
}
数组复制
int[] source = {1, 2, 3};
int[] dest = new int[3];
// 方法1:Array.Copy
Array.Copy(source, dest, source.Length);
// 方法2:Clone方法
int[] clone = (int[])source.Clone();
// 方法3:CopyTo
source.CopyTo(dest, 0);
数组排序与搜索
int[] numbers = {5, 3, 9, 1, 7};
// 快速排序
Array.Sort(numbers); // [1, 3, 5, 7, 9]
// 部分排序
Array.Sort(numbers, 1, 3); // 排序索引1开始的3个元素
// 自定义排序
Array.Sort(numbers, (x, y) => y.CompareTo(x)); // 降序[9,7,5,3,1]
// 二分查找(必须先排序)
int index = Array.BinarySearch(numbers, 7); // 返回3
if (index >= 0) {
Console.WriteLine($"找到元素,位置:{index}");
}
数组转换
// 类型转换
object[] objArray = {1, "two", 3.0};
int[] intArray = Array.ConvertAll(objArray, x => Convert.ToInt32(x));
// 使用LINQ转换
var strArray = numbers.Select(x => x.ToString()).ToArray();
7.4 数组高级
数组协变(仅适用于引用类型)
object[] objArr = new string[10]; // 合法
// objArr[0] = 5; // 运行时抛出ArrayTypeMismatchException
数组初始化器
// 多维数组初始化
int[,] matrix = {
{1, 2, 3},
{4, 5, 6}
};
// 锯齿数组初始化
int[][] jagged = {
new int[] {1, 2},
new int[] {3, 4, 5}
};
数组与Span
Span:内存中连续的数据
int[] arr = {1, 2, 3, 4, 5};
// 创建Span
Span<int> span = arr.AsSpan();
// 切片操作
Span<int> slice = span.Slice(1, 3); // [2,3,4]
slice[0] = 10; // 原数组变为[1,10,3,4,5]
数组与指针(unsafe)
unsafe {
int[] arr = {1, 2, 3};
fixed (int* ptr = arr) {
for (int i = 0; i < arr.Length; i++) {
Console.WriteLine(*(ptr + i));
}
}
}
7.5 数组实用技巧
数组比较
int[] arr1 = {1, 2, 3};
int[] arr2 = {1, 2, 3};
// 比较引用
bool sameRef = arr1 == arr2; // false
// 比较内容
bool sameContent = arr1.SequenceEqual(arr2); // true
数组填充
int[] arr = new int[5];
Array.Fill(arr, 1); // [1,1,1,1,1]
// 部分填充
Array.Fill(arr, 0, 1, 3); // [1,0,0,0,1]
数组反转
int[] arr = {1, 2, 3, 4};
Array.Reverse(arr); // [4,3,2,1]
// 部分反转
Array.Reverse(arr, 1, 2); // [4,2,3,1]
数组清空
int[] arr = {1, 2, 3};
Array.Clear(arr, 0, arr.Length); // 所有元素设为0
8.集合
8.1 集合核心概念
集合与数组的区别
| 特性 | 数组 | 集合 |
|---|---|---|
| 大小 | 固定 | 动态可变 |
| 功能 | 基础操作 | 丰富的数据操作方法 |
| 内存 | 连续存储 | 可能非连续 |
| 性能 | 访问快(O(1)) | 不同集合性能差异大 |
| 类型安全 | 同类型元素 | 泛型集合保证类型安全 |
集合分类体系
IEnumerable<T> (可枚举)
├── ICollection<T> (基础集合)
│ ├── IList<T> (有序集合)
│ └── ISet<T> (唯一性集合)
└── IDictionary<TKey,TValue> (键值集合)
8.2 List
核心特性 动态数组:基于数组自动扩容 泛型支持:类型安全 丰富API:提供排序、搜索等操作 索引访问:支持下标访问(O(1))
默认构造初始容量为0,首次添加时扩容到4, 新容量 = 旧容量 * 2 关键操作及时间复杂度
| 操作 | 方法 | 时间复杂度 |
|---|---|---|
| 添加元素 | Add(item) | 平均O(1) |
| 批量添加 | AddRange(items) | O(n) |
| 插入元素 | Insert(index,item) | O(n) |
| 删除元素 | Remove(item) | O(n) |
| 按索引删除 | RemoveAt(index) | O(n) |
| 查找元素 | Contains(item) | O(n) |
| 索引访问 | [index] | O(1) |
性能优化
// 1. 预分配容量
var list = new List<int>(10000);
// 2. 批量操作优于单个操作
list.AddRange(Enumerable.Range(1,1000));
// 3. 使用Capacity属性
list.Capacity = list.Count; // 释放多余空间
// 4. 排序优化
list.Sort(); // 快速排序 O(n log n)
8.3 Dictionary
哈希表实现原理 桶数组:存储链表头节点 哈希函数:GetHashCode()计算位置 冲突解决:链地址法处理碰撞 扩容机制:当元素数 > 容量*负载因子(默认0.75)时扩容 核心操作
Dictionary<string, int> dict = new Dictionary<string, int>();
// 添加元素(平均O(1))
dict.Add("Alice", 25);
dict["Bob"] = 30; // 替代语法
// 安全访问(避免异常)
if (dict.TryGetValue("Charlie", out int age)) {
// 处理获取的值
}
// 删除键(平均O(1))
dict.Remove("Alice");
// 遍历方式
foreach (KeyValuePair<string, int> kvp in dict) {
Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}
碰撞与性能优化
// 1. 实现良好的Equals以及GetHashCode()
public class Person
{
public string? Name { get; set; }
public int Age { get; set; }
public override bool Equals(object? obj)
{
return obj is Person person &&
Name == person.Name &&
Age == person.Age;
}
public override int GetHashCode()
{
return HashCode.Combine(Name, Age);
}
}
// 2. 设置初始容量
var bigDict = new Dictionary<string, int>(100000);
// 3. 避免频繁扩容
dict.EnsureCapacity(50000);
8.4 HashSet 与 SortedSet
HashSet 特性:
- 唯一元素:自动去重
- 哈希实现:基于Dictionary实现
- 集合运算:支持并/交/差集
HashSet<int> setA = new HashSet<int>{1, 2, 3};
HashSet<int> setB = new HashSet<int>{3, 4, 5};
// 集合运算
setA.UnionWith(setB); // 并集 {1,2,3,4,5}
setA.IntersectWith(setB); // 交集 {3}
setA.ExceptWith(setB); // 差集 {1,2}
SortedSet
- 红黑树实现:元素自动排序
- 范围查询:支持获取子集
- 查找效率:O(log n)
SortedSet<int> sorted = new SortedSet<int>{5, 3, 9};
// 获取范围视图
var subset = sorted.GetViewBetween(3, 7); // [3,5]
// 最小最大值
int min = sorted.Min; // 3
int max = sorted.Max; // 9
8.5 Queue与Stack
队列Queue
Queue<string> queue = new Queue<string>();
// 入队
queue.Enqueue("First");
queue.Enqueue("Second");
// 出队
string item = queue.Dequeue(); // "First"
// 查看队首
string peek = queue.Peek(); // "Second"
栈Stack
Stack<int> stack = new Stack<int>();
// 压栈
stack.Push(1);
stack.Push(2);
// 弹栈
int top = stack.Pop(); // 2
// 查看栈顶
int current = stack.Peek(); // 1
8.6 线程安全集合
Concurrent Collections
// 1. 并发字典
ConcurrentDictionary<int, string> concurrentDict = new ConcurrentDictionary<int, string>();
concurrentDict.TryAdd(1, "Value");
// 2. 并发队列
ConcurrentQueue<int> concurrentQueue = new ConcurrentQueue<int>();
concurrentQueue.Enqueue(10);
// 3. 并发栈
ConcurrentStack<int> concurrentStack = new ConcurrentStack<int>();
concurrentStack.Push(20);
// 4. 并发包(无序集合)
ConcurrentBag<int> concurrentBag = new ConcurrentBag<int>();
concurrentBag.Add(30);
同步包装器
// 创建线程安全集合
var syncList = System.Collections.Synchronized(new List<int>());
var syncDict = System.Collections.Synchronized(new Dictionary<int, string>());
// 使用时仍需lock保证原子操作
lock (syncList.SyncRoot) {
syncList.Add(1);
}
8.7 性能优化
预分配容量:减少扩容操作
new List<int>(1000);
new Dictionary<string,int>(5000);
避免装箱拆箱:使用泛型集合
// 错误:ArrayList导致装箱
ArrayList badList = new ArrayList();
badList.Add(1); // 装箱
// 正确:List<T>避免装箱
List<int> goodList = new List<int>();
goodList.Add(1); // 无装箱
批量操作:减少方法调用开销
// 优于多次Add
list.AddRange(items);
LINQ延迟执行:及时物化查询
// 立即执行
var result = source.Where(x => x > 0).ToList();
9.字符串
9.1 字符串基础
9.1.1 字符串特性
- 不可变性(Immutable):字符串一旦创建就不能修改,所有"修改"操作都返回新字符串
- 引用类型:但具有值类型的某些特性
- Unicode编码:支持多语言字符
- 字符串池(String Interning):CLR对字面量字符串的优化机制
9.1.2 字符串创建方式
// 1. 直接赋值
string s1 = "Hello World";
// 2. 使用构造函数
char[] letters = { 'H', 'e', 'l', 'l', 'o' };
string s2 = new string(letters); // "Hello"
// 3. 重复字符
string s3 = new string('a', 5); // "aaaaa"
// 4. 从指针创建(unsafe)
unsafe {
fixed (char* p = letters) {
string s4 = new string(p); // "Hello"
}
}
9.2 字符串常用操作
9.2.1 基本操作
// 长度获取
int len = "Hello".Length; // 5
// 索引访问
char first = "Hello"[0]; // 'H'
// 连接字符串
string concat = "Hello" + " " + "World"; // "Hello World"
// 大小写转换
string upper = "Hello".ToUpper(); // "HELLO"
string lower = "Hello".ToLower(); // "hello"
9.2.2 字符串比较
// 1. 值比较
bool equal = "Hello".Equals("hello"); // false
bool ignoreCase = "Hello".Equals("hello", StringComparison.OrdinalIgnoreCase); // true
// 2. Compare方法
int result = string.Compare("A", "B"); // -1 (A < B)
// 3. CompareTo方法
int result2 = "A".CompareTo("B"); // -1
// 4. 运算符
bool opEqual = "A" == "A"; // true
9.2.3 字符串查找
string text = "Hello World";
// 查找索引
int index1 = text.IndexOf('o'); // 4
int index2 = text.IndexOf("World"); // 6
int index3 = text.LastIndexOf('o'); // 7
// 检查包含
bool contains = text.Contains("World"); // true
// 开头/结尾检查
bool starts = text.StartsWith("Hello"); // true
bool ends = text.EndsWith("ld"); // true
9.2.4 字符串修改
string original = "Hello World";
// 截取
string sub1 = original.Substring(6); // "World"
string sub2 = original.Substring(0, 5); // "Hello"
// 插入
string inserted = original.Insert(5, " Beautiful"); // "Hello Beautiful World"
// 移除
string removed = original.Remove(5, 6); // "Hello"
// 替换
string replaced = original.Replace("World", "C#"); // "Hello C#"
// 修剪空白
string trimmed = " Hello ".Trim(); // "Hello"
string trimStart = " Hello ".TrimStart(); // "Hello "
string trimEnd = " Hello ".TrimEnd(); // " Hello"
9.3 字符串格式化
9.3.1 复合格式化
string.Format("Name: {0}, Age: {1}", "Alice", 25);
// "Name: Alice, Age: 25"
9.3.2 字符串插值(C# 6.0+)
string name = "Alice";
int age = 25;
string result = $"Name: {name}, Age: {age}";
// "Name: Alice, Age: 25"
9.3.3 数值格式化
// 数字格式化
string.Format("{0:N2}", 1234.5678); // "1,234.57"
string.Format("{0:D5}", 42); // "00042"
string.Format("{0:C}", 19.99); // "$19.99"
// 日期格式化
DateTime now = DateTime.Now;
string.Format("{0:yyyy-MM-dd}", now); // "2023-06-15"
9.4 字符串高级特性
9.4.1 字符串构建器(StringBuilder)
StringBuilder sb = new StringBuilder();
sb.Append("Hello");
sb.AppendLine(" World");
sb.AppendFormat("Today is {0:yyyy-MM-dd}", DateTime.Now);
string result = sb.ToString();
9.4.2 字符串池(Interning)
string a = "Hello";
string b = "Hello";
string c = new string(new[] { 'H', 'e', 'l', 'l', 'o' });
Console.WriteLine(object.ReferenceEquals(a, b)); // True (字符串池)
Console.WriteLine(object.ReferenceEquals(a, c)); // False
// 手动驻留
string d = string.Intern(c);
Console.WriteLine(object.ReferenceEquals(a, d)); // True
9.4.3 逐字字符串(@)与转义字符
// 普通字符串
string path1 = "C:\\Windows\\System32"; // 需要转义
// 逐字字符串
string path2 = @"C:\Windows\System32"; // 不需要转义
// 多行字符串
string multiLine = @"Line 1
Line 2
Line 3";
9.5 字符串编码与转换
9.5.1 编码转换
// 字符串转字节数组
byte[] utf8Bytes = Encoding.UTF8.GetBytes("Hello");
byte[] unicodeBytes = Encoding.Unicode.GetBytes("Hello");
// 字节数组转字符串
string fromUtf8 = Encoding.UTF8.GetString(utf8Bytes);
string fromUnicode = Encoding.Unicode.GetString(unicodeBytes);
9.5.2 Base64编码
// 编码
string base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes("Hello"));
// 解码
string original = Encoding.UTF8.GetString(Convert.FromBase64String(base64));
9.6 字符串性能优化
9.6.1 避免不必要的字符串操作
// 不好:创建多个临时字符串
string result = "";
for (int i = 0; i < 100; i++) {
result += i.ToString();
}
// 好:使用StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; i++) {
sb.Append(i);
}
string finalResult = sb.ToString();
9.6.2 比较优化
// 不好:区分大小写的比较
if (input.ToLower() == "admin") { ... }
// 好:使用比较选项
if (input.Equals("admin", StringComparison.OrdinalIgnoreCase)) { ... }
9.6.3 字符串分割优化
// 不好:多次分割
string[] parts1 = csv.Split(',');
string[] parts2 = csv.Split(';');
// 好:一次性分割
char[] separators = { ',', ';' };
string[] parts = csv.Split(separators);
9.7 正则表达式
9.7.1 基本用法
using System.Text.RegularExpressions;
string pattern = @"\d+"; // 匹配数字
Regex regex = new Regex(pattern);
bool isMatch = regex.IsMatch("abc123"); // true
Match match = regex.Match("abc123");
string value = match.Value; // "123"
9.7.2 常用模式
// 电子邮件验证
bool isEmail = Regex.IsMatch(input, @"^[^@\s]+@[^@\s]+\.[^@\s]+$");
// 提取数字
MatchCollection matches = Regex.Matches("a1b2c3", @"\d");
foreach (Match m in matches) {
Console.WriteLine(m.Value); // 1, 2, 3
}
// 替换
string clean = Regex.Replace("a1b2c3", @"\d", "_"); // "a_b_c_"
9.8 字符串安全
敏感信息处理
// 使用SecureString处理密码等敏感信息
SecureString securePwd = new SecureString();
foreach (char c in "password") {
securePwd.AppendChar(c);
}
// 使用后立即清除
securePwd.Dispose();
10.控制台(Console )
10.1 基础
10.1.1 Console 概述 System.Console 类提供对标准输入、输出和错误流的访问 主要用于控制台应用程序的输入输出操作 所有方法都是静态的,无需实例化 10.1.2 基本输入输出 输出文本
Console.Write("Hello"); // 不换行输出
Console.WriteLine("World"); // 输出后换行
读取输入
string input = Console.ReadLine(); // 读取一行输入
int key = Console.Read(); // 读取单个字符(返回ASCII码)
ConsoleKeyInfo keyInfo = Console.ReadKey(); // 读取按键信息
10.2 格式化输出
10.2.1 复合格式化
string name = "Alice";
int age = 25;
Console.WriteLine("Name: {0}, Age: {1}", name, age);
10.2.2 字符串插值(C# 6.0+)
Console.WriteLine($"Name: {name}, Age: {age}");
10.2.3 数值格式化
double num = 1234.5678;
Console.WriteLine("{0:N2}", num); // 1,234.57
Console.WriteLine("{0:C}", 19.99); // $19.99
Console.WriteLine("{0:D5}", 42); // 00042
10.3 控制台颜色设置
10.3.1 颜色属性
Console.ForegroundColor = ConsoleColor.Red; // 前景色(文本)
Console.BackgroundColor = ConsoleColor.White; // 背景色
Console.ResetColor(); // 重置为默认颜色
10.3.2 颜色示例
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("警告信息");
Console.ResetColor();
10.4 控制台窗口控制
10.4.1 窗口属性 可能只在windows生效
Console.WindowWidth = 80; // 设置窗口宽度
Console.WindowHeight = 30; // 设置窗口高度
Console.BufferWidth = 100; // 设置缓冲区宽度
Console.BufferHeight = 1000; // 设置缓冲区高度
Console.Title = "我的控制台"; // 设置窗口标题
10.4.2 光标控制
Console.CursorVisible = false; // 隐藏光标
Console.SetCursorPosition(10, 5); // 设置光标位置(列,行)
Console.CursorLeft = 20; // 设置光标列位置
Console.CursorTop = 10; // 设置光标行位置
10.5 高级功能
10.5.1 控制台铃声
Console.Beep(); // 默认频率和时长
Console.Beep(800, 200); // 频率(Hz), 时长(ms)
10.5.2 编码设置
Console.OutputEncoding = Encoding.UTF8; // 设置输出编码
Console.InputEncoding = Encoding.UTF8; // 设置输入编码
10.5.3 清屏与进度显示
Console.Clear(); // 清空控制台
// 简单进度条
for (int i = 0; i <= 100; i++)
{
Console.Write($"\r进度: {i}% "); // \r回到行首
Thread.Sleep(50);
}
11.异常处理
11.1 异常处理基础
11.1.1 异常概念 异常:程序执行期间发生的意外情况
异常类:所有异常都继承自System.Exception
异常处理:捕获并处理异常,防止程序崩溃
11.1.2 常见异常类型
| 异常类型 | 描述 |
|---|---|
| NullReferenceException | 尝试访问null对象成员 |
| IndexOutOfRangeException | 数组/集合索引越界 |
| ArgumentException | 方法参数无效 |
| FormatException | 格式转换失败 |
| IOException | I/O操作错误 |
| DivideByZeroException | 除零错误 |
11.2 基本异常处理结构
** 11.2.1 try-catch 块**
try
{
// 可能抛出异常的代码
int result = 10 / int.Parse("0");
}
catch (DivideByZeroException ex)
{
Console.WriteLine($"除零错误: {ex.Message}");
}
catch (FormatException ex)
{
Console.WriteLine($"格式错误: {ex.Message}");
}
11.2.2 finally
FileStream file = null;
try
{
file = File.Open("test.txt", FileMode.Open);
// 文件操作
}
catch (IOException ex)
{
Console.WriteLine($"IO错误: {ex.Message}");
}
finally
{
file?.Close(); // 确保资源释放
}
11.2.3 throw 语句
// 重新抛出当前异常
catch (Exception ex)
{
Console.WriteLine("记录错误");
throw; // 保留原始调用栈
}
// 抛出新异常
if (value < 0)
throw new ArgumentException("值不能为负");
11.3 异常处理高级特性
11.3.1 异常筛选器 (C# 6.0+)
try { /* ... */ }
catch (Exception ex) when (ex.Message.Contains("特定错误"))
{
// 仅当条件满足时捕获
}
11.3.2 自定义异常
public class InvalidAccountException : Exception
{
public InvalidAccountException() { }
public InvalidAccountException(string message) : base(message) { }
public InvalidAccountException(string message, Exception inner)
: base(message, inner) { }
}
// 使用自定义异常
throw new InvalidAccountException("账户无效");
11.3.3 异常数据字典
try { /* ... */ }
catch (Exception ex)
{
ex.Data.Add("Timestamp", DateTime.Now);
ex.Data.Add("User", "Admin");
throw;
}
11.4 异常处理与资源管理
11.4.1 using 语句
using (var resource = new DisposableResource())
{
// 自动调用Dispose(),即使抛出异常
}
11.4.2 模式匹配 (C# 7.0+)
try { /* ... */ }
catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException)
{
Console.WriteLine("文件访问错误");
}
二.面向对象编程
1.类与对象
1.1 类和对象的基本概念
类 (Class)
- 类是创建对象的蓝图或模板
- 定义了对象的属性和行为
- 是面向对象编程的基本构建块
对象 (Object)
- 对象是类的实例
- 每个对象都有自己的状态(属性值)和行为(方法)
- 对象通过new关键字创建
// 定义一个类
public class Person
{
// 类的成员
}
// 创建对象
Person person1 = new Person();
1.2 类的成员
1.2.1 字段 (Fields)
- 存储类或对象的数据
- 通常设为私有(private),通过属性访问
private string _name;
private int _age;
1.2.2 属性 (Properties)
- 提供对字段的安全访问
- 可以包含get和set访问器
private string _name;
public string Name
{
get { return _name; }
set { _name= value; }
}
// 自动属性(C# 3.0+)
public int Age { get; set; }
1.2.3 方法 (Methods)
- 定义类的行为
- 可以接受参数并返回值
public void SayHello()
{
Console.WriteLine($"Hello, my name is {Name}");
}
public int CalculateAgeInMonths()
{
return Age * 12;
}
1.2.4 构造函数 (Constructors)
- 用于初始化对象
- 与类同名,没有返回类型
- 可以重载
public Person()
{
// 默认构造函数
}
public Person(string name, int age)
{
Name = name;
Age = age;
}
1.3 文档注释
为公共方法,属性添加注释,该注释能辅助编写代码时鼠标放上去查看解释
/// <summary>
/// 计算两个数的和
/// </summary>
/// <param name="a">第一个加数</param>
/// <param name="b">第二个加数</param>
/// <returns>两数之和</returns>
public int Add(int a, int b)
{
return a + b;
}
1.4 访问修饰符
| 关键字 | 描述 |
|---|---|
| public | 访问不受限制 |
| private | 只能在当前类中访问 |
| protected | 只能在当前类或派生类中访问 |
| internal | 只能在当前程序集中访问 |
| protected internal | 在当前程序集或派生类中可访问 |
| private protected | 在当前程序集的派生类中可访问 |
| file | 仅在当前文件中可访问 |
1.5 静态成员
- 属于类而不是对象
- 通过类名访问,而不是对象实例
- 常用于工具类或共享数据
public class MathUtility
{
public static double PI = 3.14159;
public static int Add(int a, int b)
{
return a + b;
}
}
// 使用静态成员
double pi = MathUtility.PI;
int sum = MathUtility.Add(5, 3);
1.6 静态类
静态类是一种特殊的类,具有以下核心特征:
- 不能被实例化(不能使用new创建对象)
- 只能包含静态成员(静态字段、静态方法、静态属性等)
- 隐式密封:静态类隐式是sealed的,尝试继承会编译错误
public static class MathUtility
{
public static double PI = 3.14159;
}
// 使用静态成员
double pi = MathUtility.PI;
1.7 静态构造函数
- 在首次访问类时自动执行
- 每个应用程序域只执行一次
- 没有访问修饰符和参数
public class Logger
{
private static readonly string logFile;
// 静态构造函数
static Logger()
{
logFile = $"log_{DateTime.Now:yyyyMMdd}.txt";
Console.WriteLine("Logger初始化完成");
}
public static void Log(string message) { /*...*/ }
}
1.8 析构函数/终结器
- 在对象销毁前执行清理操作
- 不能手动调用,由垃圾回收器自动调用
~Person()
{
// 清理代码
}
现代对象销毁设计
public class Resource : IDisposable
{
public void Dispose()
{
// 确定性释放资源
}
}
// 使用示例
using (var res = new Resource())
{
// 自动调用Dispose()
}
1.9 ref、out
1.9.1 值传递 vs 引用传递 默认行为:C# 方法参数默认是值传递(传递副本)
void Modify(int x) { x = 100; }
int num = 10;
Modify(num); // num 仍然是10
1.9.2 ref 关键字 实现引用传递,方法内修改会影响原始变量,调用前必须初始化变量,方法声明和调用时都要加 ref。
void ModifyRef(ref int x) { x = 100; }
int num = 10;
ModifyRef(ref num); // num 变为100
1.9.3 out 关键字 用于从方法返回多个值(类似引用传递),调用前无需初始化变量,方法内必须赋值给out参数,方法声明和调用时都要加 out。
bool TryParse(string s, out int result) {
if (int.TryParse(s, out result)) {
return true;
}
return false;
}
int parsedValue;
if (TryParse("123", out parsedValue)) {
Console.WriteLine(parsedValue); // 输出123
}
1.10 运算符重载
public class Vector2D
{
public double X { get; set; }
public double Y { get; set; }
public Vector2D(double x, double y)
{
X = x;
Y = y;
}
public static Vector2D operator +(Vector2D a, Vector2D b)
{
return new Vector2D(a.X + b.X, a.Y + b.Y);
}
public static Vector2D operator -(Vector2D a, Vector2D b)
{
return new Vector2D(a.X - b.X, a.Y - b.Y);
}
}
public static void Main(string[] args)
{
Vector2D v1 = new Vector2D(1, 2);
Vector2D v2 = new Vector2D(3, 4);
var n = (v1 + v2);
Console.WriteLine($"new x:{n.X},new y:{n.Y}");
}
1.11 this关键字
1.11.1 类索引器 索引器(Indexer)是 C# 中的一种特殊成员,它允许类的实例像数组一样通过索引来访问。索引器类似于属性,但使用索引参数而不是属性名。
public class StringArray
{
private string[] array = new string[10];
// 索引器定义
public string this[int index]
{
get
{
if (index < 0 || index >= array.Length)
throw new IndexOutOfRangeException();
return array[index];
}
set
{
if (index < 0 || index >= array.Length)
throw new IndexOutOfRangeException();
array[index] = value;
}
}
}
// 使用示例
StringArray myArray = new StringArray();
myArray[0] = "Hello"; // 调用set访问器
Console.WriteLine(myArray[0]); // 调用get访问器,输出"Hello"
public class Matrix
{
private int[,] data = new int[10, 10];
public int this[int row, int col]
{
get { return data[row, col]; }
set { data[row, col] = value; }
}
}
// 使用示例
Matrix matrix = new Matrix();
matrix[0, 0] = 1;
Console.WriteLine(matrix[0, 0]); // 输出1
1.11.2 链式构造和指代当前
public class User
{
string uuid;
string username;
string password;
public User()
{
uuid = Guid.NewGuid().ToString();
}
/// <summary>
/// :this() 调用默认无参构造函数
/// </summary>
/// <param name="username"></param>
public User(string username):this()
{
// 指示前传入的username赋值给当对象的username
this.username = username;
}
public User(string username,string password):this(username)
{
this.password = password;
}
}
1.11.3 类拓展
public class Program
{
public static void Main(string[] args)
{
List<string> breakfast = new List<string>()
{
"煎饼果子",
"包子",
"油条",
"豆浆",
"稀饭",
"炒饭",
"炒面",
"蛋挞",
"牛奶",
"面包",
};
Console.WriteLine($"今早吃:{breakfast.GetRandomItem()}");
}
}
public static class ListExpand
{
private readonly static Random random=new Random();
public static T GetRandomItem<T>(this List<T> list)
{
return list[random.Next(0, list.Count)];
}
}
1.12 部分类
将一个类分为两个文件编写,编译后为一个类
// File1.cs
public partial class MyClass
{
public void Method1() { }
}
// File2.cs
public partial class MyClass
{
public void Method2() { }
}
1.13 对象初始化器
// Name 和 Age 为公开可写的属性
Person person = new Person
{
Name = "John",
Age = 30
};
1.14 只读属性和只读字段
public class MyClass
{
/// <summary>
/// 只读字段:只能进行读取,无法重新赋值,且只能在构造函数中或字段上初始化
/// </summary>
private readonly List<string> _list = new List<string>();
private readonly List<string> _list2;
/// <summary>
/// 只读属性:外部只能读取,不能修改
/// </summary>
public List<string> List3 { get; } = new List<string>();
public MyClass()
{
_list2 = new List<string>();
}
}
2.面向对象
2.1 面向对象三大特性
- 封装:隐藏对象内部细节,仅暴露必要接口
- 继承:子类继承父类特征和行为
- 多态:同一操作作用于不同对象产生不同行为
2.2 封装
两个核心思想:
- 数据隐藏:将对象的内部状态(数据)隐藏起来,不允许外部直接访问
- 行为暴露:通过公共方法(接口)提供对数据的受控访问
简单的封装示例:
public class User
{
/// <summary>
/// 自动属性,仅限内部使用
/// </summary>
public int Id { get; private set; }
/// <summary>
/// 私有字段,外部无法访问
/// </summary>
private string _username;
/// <summary>
/// 提供公开的访问属性进行暴露
/// </summary>
public string Username{
get => _username;
}
/// <summary>
/// 通过方法进行暴露
/// </summary>
/// <returns></returns>
public string GetUsername()
{
return _username;
}
/// <summary>
/// 设置用户名
/// </summary>
public void SetUsername(string username,string code)
{
if(string.IsNullOrEmpty(username)||string.IsNullOrEmpty(code)||!string.Equals(code,"123456"))
{
throw new ArgumentException("Username错误");
}
_username = username;
}
}
2.3 继承
基于现有类创建新类,实现代码的重用和扩展
- 基类/父类:被继承的类
- 派生类/子类:继承自基类的新类
- "is-a"关系:子类是父类的一种特殊类型
继承的基本语法
// 基类(父类)
public class Animal
{
public string Name { get; set; }
public void Eat()
{
Console.WriteLine($"{Name} is eating.");
}
}
// 派生类(子类)
public class Dog : Animal // 使用冒号表示继承
{
public void Bark()
{
Console.WriteLine($"{Name} is barking.");
}
}
成员继承
1.子类自动获得父类的所有非私有成员(字段、属性、方法) 2.私有成员(private)不会被继承
构造函数继承
public class Animal
{
public string Name { get; }
// 基类构造函数
public Animal(string name)
{
Name = name;
}
}
public class Dog : Animal
{
public string Breed { get; }
// 派生类构造函数必须调用基类构造函数
public Dog(string name, string breed) : base(name) // 使用base关键字
{
Breed = breed;
}
}
方法重写(虚方法)
public class Animal
{
public virtual void MakeSound() // virtual关键字表示可重写
{
Console.WriteLine("Some generic animal sound");
}
}
public class Dog : Animal
{
public override void MakeSound() // override关键字表示重写
{
Console.WriteLine("Woof! Woof!");
}
}
// 使用
Animal myDog = new Dog();
myDog.MakeSound(); // 输出 "Woof! Woof!" (多态)
隐藏方法(new关键字)
public class Animal
{
public void Sleep()
{
Console.WriteLine("Animal is sleeping");
}
}
public class Dog : Animal
{
public new void Sleep() // new关键字隐藏基类方法
{
Console.WriteLine("Dog is sleeping");
}
}
// 使用
Animal animal = new Dog();
animal.Sleep(); // 输出 "Animal is sleeping" (没有多态)
Dog dog = new Dog();
dog.Sleep(); // 输出 "Dog is sleeping"
只有重写才有多态效果,方法重叠和隐藏方法都不行
密封类和密封方法
public sealed class FinalClass // sealed关键字表示不能被继承
{
// 类成员
}
public class BaseClass
{
public virtual void Method()
{
// 可重写的方法
}
}
public class DerivedClass : BaseClass
{
public sealed override void Method() // 密封方法,不能再被重写
{
// 方法实现
}
}
多继承与接口 C#不支持多类继承,但可以通过接口实现
public interface IMovable
{
void Move();
}
public interface ISpeakable
{
void Speak();
}
public class Robot : IMovable, ISpeakable // 实现多个接口
{
public void Move()
{
Console.WriteLine("Robot is moving");
}
public void Speak()
{
Console.WriteLine("Beep boop");
}
}
2.4 多态
编译时多态(方法重载)
public class Calculator
{
// 方法重载:相同方法名,不同参数列表
public int Add(int a, int b)
{
return a + b;
}
public double Add(double a, double b)
{
return a + b;
}
public int Add(int a, int b, int c)
{
return a + b + c;
}
}
// 使用
var calc = new Calculator();
Console.WriteLine(calc.Add(1, 2)); // 调用int版本
Console.WriteLine(calc.Add(1.5, 2.5)); // 调用double版本
Console.WriteLine(calc.Add(1, 2, 3)); // 调用三参数版本
运行时多态(方法重写)
3.接口
接口是C#中一种引用类型,它定义了一组相关功能的契约(合同),但不提供具体实现
- 纯抽象:只包含方法、属性、事件和索引器的签名
- 多继承:类可以实现多个接口
- 解耦:分离定义与实现,降低耦合度
- 多态:实现接口的不同类可以以统一方式处理
3.1 定义接口
public interface ILogger
{
// 方法签名
void Log(string message);
// 属性签名
string LogLevel { get; set; }
// 事件签名
event Action<string> OnLogged;
// 默认实现(C# 8.0+)
void LogError(string error)
{
Log($"[ERROR] {error}");
OnLogged?.Invoke(error);
}
}
3.2 实现接口
public class FileLogger : ILogger
{
private string _logLevel = "INFO";
public string LogLevel
{
get => _logLevel;
set => _logLevel = value;
}
public event Action<string> OnLogged;
public void Log(string message)
{
File.AppendAllText("log.txt", $"[{LogLevel}] {DateTime.Now}: {message}\n");
OnLogged?.Invoke(message);
}
// 可以选择重写默认实现
public void LogError(string error)
{
Log($"[CRITICAL] {error}");
}
}
3.3 显式接口实现
解决多个接口有相同成员名的问题
public interface IDatabase
{
void Connect();
}
public interface IWebService
{
void Connect();
}
public class DataService : IDatabase, IWebService
{
// 显式实现IDatabase.Connect
void IDatabase.Connect()
{
Console.WriteLine("Connecting to database...");
}
// 显式实现IWebService.Connect
void IWebService.Connect()
{
Console.WriteLine("Connecting to web service...");
}
// 普通方法
public void Connect()
{
Console.WriteLine("General connection...");
}
}
// 使用
var service = new DataService();
service.Connect(); // 调用普通方法
((IDatabase)service).Connect(); // 调用IDatabase实现
((IWebService)service).Connect(); // 调用IWebService实现
3.4 接口继承
public interface IShape
{
double CalculateArea();
}
public interface IDrawable : IShape
{
void Draw();
}
public class Circle : IDrawable
{
public double Radius { get; set; }
public double CalculateArea()
{
return Math.PI * Radius * Radius;
}
public void Draw()
{
}
}
3.5 默认接口方法(C# 8.0+)
public interface IOrder
{
decimal Amount { get; }
DateTime Date { get; }
// 默认实现
string GetOrderInfo()
{
return $"Order on {Date:d} for {Amount:C}";
}
}
public class OnlineOrder : IOrder
{
public decimal Amount { get; set; }
public DateTime Date { get; set; }
// 可以选择不实现GetOrderInfo
}
// 使用
IOrder order = new OnlineOrder { Amount = 100.50m, Date = DateTime.Now };
Console.WriteLine(order.GetOrderInfo()); // 使用接口默认实现
4.抽象类
抽象类是C#中一种特殊的类,它不能被实例化,只能被继承。 1.不能被实例化:只能作为基类 2.可以包含抽象成员:没有实现的成员 3.可以包含具体实现:非抽象成员可以有实现 4.介于接口和普通类之间:比接口更具体,比普通类更抽象
4.1 定义抽象类
public abstract class Shape
{
// 抽象方法(没有实现)
public abstract double CalculateArea();
// 普通方法(有实现)
public void Display()
{
Console.WriteLine($"Area: {CalculateArea()}");
}
// 抽象属性
public abstract string Name { get; }
// 普通属性
public string Color { get; set; } = "Black";
}
4.2 继承抽象类
public class Circle : Shape
{
public double Radius { get; set; }
// 必须实现抽象成员
public override double CalculateArea()
{
return Math.PI * Radius * Radius;
}
public override string Name => "Circle";
// 可以选择重写普通方法
public override void Display()
{
Console.WriteLine($"Circle with radius {Radius} has area {CalculateArea():F2}");
}
}
4.3 抽象类与构造方法
public abstract class Animal
{
public string Species { get; }
// 抽象类可以有构造方法
protected Animal(string species)
{
Species = species;
}
public abstract void MakeSound();
}
public class Dog : Animal
{
public Dog() : base("Canine") { }
public override void MakeSound()
{
Console.WriteLine("Woof!");
}
}
4.4 抽象类与密封方法
public abstract class Vehicle
{
public abstract void StartEngine();
// 密封方法(在派生类中不能被重写)
public sealed void StopEngine()
{
Console.WriteLine("Engine stopped");
}
}
public class Car : Vehicle
{
public override void StartEngine()
{
Console.WriteLine("Car engine started");
}
// 不能重写StopEngine方法
}
4.5 抽象类与虚方法
public abstract class Database
{
// 抽象方法(必须被重写)
public abstract void Connect();
// 虚方法(可以被重写)
public virtual void Disconnect()
{
Console.WriteLine("Disconnected from database");
}
}
public class SqlDatabase : Database
{
public override void Connect()
{
Console.WriteLine("Connected to SQL Server");
}
// 可以选择重写虚方法
public override void Disconnect()
{
Console.WriteLine("SQL Server connection closed");
base.Disconnect(); // 调用基类实现
}
}
4.6 抽象类与接口的比较
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 实例化 | 不能 | 不能 |
| 默认实现 | 可以有 | C# 8.0+可以有默认实现 |
| 字段 | 可以包含实例字段 | 不能 |
| 构造方法 | 可以有 | 不能 |
| 访问修饰符 | 可以控制成员访问级别 | 默认为public |
| 多继承 | 不支持 | 支持 |
| 版本控制 | 添加新方法不影响现有子类 | 添加新成员会破坏现有实现 |
5.委托
5.1 描述
- 类型安全的函数指针:委托是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用
- 回调机制:允许将方法作为参数传递
- 多播能力:一个委托可以引用多个方法
5.2 委托的声明与使用
using static TestDelegateDemo;
public class Program
{
public static void Main(string[] args)
{
TestDelegateDemo testDelegateDemo = new TestDelegateDemo();
testDelegateDemo.del += new MyDelegate(ShowMessage);
testDelegateDemo.del += new MyDelegate(ShowMessage2);
testDelegateDemo.del += new MyDelegate(ShowMessage3);
// 也可进行删除
// testDelegateDemo.del -= new MyDelegate(ShowMessage2);
// 匿名方法
testDelegateDemo.del += delegate (string msg)
{
Console.WriteLine("匿名方法:" + msg);
};
// Lambda表达式
testDelegateDemo.del += (msg) =>
{
Console.WriteLine("Lambda表达式:" + msg);
};
testDelegateDemo.ShowAllMessage();
// 直接调用
//testDelegateDemo.del?.Invoke("Hello, World!");
}
public static void ShowMessage(string msg)
{
Console.WriteLine("sm1:"+msg);
}
public static void ShowMessage2(string msg)
{
Console.WriteLine("sm2:" + msg);
}
public static void ShowMessage3(string msg)
{
Console.WriteLine("sm3:" + msg);
}
}
public class TestDelegateDemo
{
public delegate void MyDelegate(string message);
public MyDelegate? del;
public void ShowAllMessage()
{
del?.Invoke("Hello, World!");
}
}
5.3 内置委托类型
C# 提供了几种内置的通用委托类型,避免重复声明:
- Action:无返回值的方法(最多16个参数)
- Func:有返回值的方法(最多16个参数)
- Predicate:返回bool的方法(1个参数)
// 使用Action
Action<string> action = msg => Console.WriteLine(msg);
action("Hello Action!");
// 使用Func
Func<int, int, int> add = (a, b) => a + b;
int result = add(3, 5); // 8
// 使用Predicate
Predicate<int> isEven = num => num % 2 == 0;
bool even = isEven(4); // true
6.事件
6.1 描述
- 基于委托的发布-订阅模型:事件是委托的一种特殊形式,用于实现观察者模式
- 封装性:事件限制了外部对委托的直接访问
- 安全机制:防止外部直接调用或重置委托
6.2 标准事件模式
完整实现:
public class Program
{
public static void Main(string[] args)
{
var monitor = new TemperatureMonitor();
monitor.TemperatureChanged += (sender, e) =>
{
Console.WriteLine($"温度变化: {e.OldTemperature}°C -> {e.NewTemperature}°C");
};
monitor.CurrentTemperature = 10.0f;
monitor.CurrentTemperature = 36.0f;
}
}
/// <summary>
/// 气温监听器
/// </summary>
public class TemperatureMonitor
{
public delegate void TemperatureChangedHandler(object sender, TemperatureEventArgs e);
public event TemperatureChangedHandler? TemperatureChanged;
private float _currentTemp;
public float CurrentTemperature
{
get => _currentTemp;
set
{
if (_currentTemp != value)
{
float oldTemp = _currentTemp;
_currentTemp = value;
//触发事件
OnTemperatureChanged(oldTemp, _currentTemp);
}
}
}
protected virtual void OnTemperatureChanged(float oldTemp, float newTemp)
{
TemperatureChanged?.Invoke(this, new TemperatureEventArgs(oldTemp, newTemp));
}
}
public class TemperatureEventArgs : EventArgs
{
public float OldTemperature { get; }
public float NewTemperature { get; }
public TemperatureEventArgs(float oldTemp, float newTemp)
{
OldTemperature = oldTemp;
NewTemperature = newTemp;
}
}
简单实现
public class Program
{
public static void Main(string[] args)
{
var monitor = new TemperatureMonitor();
monitor.TemperatureChanged += (sender, e) =>
{
Console.WriteLine($"温度变化: {e.OldTemperature}°C -> {e.NewTemperature}°C");
};
monitor.CurrentTemperature = 10.0f;
monitor.CurrentTemperature = 36.0f;
}
}
/// <summary>
/// 气温监听器
/// </summary>
public class TemperatureMonitor
{
public event EventHandler<TemperatureEventArgs>? TemperatureChanged;
private float _currentTemp;
public float CurrentTemperature
{
get => _currentTemp;
set
{
if (_currentTemp != value)
{
float oldTemp = _currentTemp;
_currentTemp = value;
//触发事件
OnTemperatureChanged(oldTemp, _currentTemp);
}
}
}
protected virtual void OnTemperatureChanged(float oldTemp, float newTemp)
{
TemperatureChanged?.Invoke(this, new TemperatureEventArgs(oldTemp, newTemp));
}
}
public class TemperatureEventArgs : EventArgs
{
public float OldTemperature { get; }
public float NewTemperature { get; }
public TemperatureEventArgs(float oldTemp, float newTemp)
{
OldTemperature = oldTemp;
NewTemperature = newTemp;
}
}
6.3 事件与委托的关键区别
| 特性 | 委托 | 事件 |
|---|---|---|
| 访问控制 | 可被外部直接调用和赋值 | 只能在声明类内部触发 |
| 多播能力 | 支持 | 支持 |
| 外部订阅/取消 | 可以 | 只能通过+=/-=操作 |
| 用途 | 通用回调机制 | 实现观察者模式 |
| 安全性 | 低(可能被外部重置) | 高(封装了委托) |
7.结构体
结构体(struct)是C#中的一种值类型,与类(class)这种引用类型有着本质区别。
7.1 结构体基本概念
定义语法
public struct Point
{
public int X;
public int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
public void Move(int deltaX, int deltaY)
{
X += deltaX;
Y += deltaY;
}
}
核心特性:
- 值类型:存储在栈上(通常),直接包含数据
- 默认构造函数:编译器自动生成无参构造函数,不能自定义
- 继承限制:不能继承其他结构体或类,只能实现接口
- 不可为null:除非声明为可空类型(Nullable
或Point?) - 隐式密封:结构体隐式是密封的(sealed)
7.2 结构体与类的区别
| 特性 | 结构体 | 类 |
|---|---|---|
| 类型 | 值类型 | 引用类型 |
| 存储位置 | 栈(通常) | 堆 |
| 继承 | 只能实现接口 | 支持单继承和多接口实现 |
| 默认构造函数 | 自动生成,不能自定义 | 可以自定义 |
| 析构函数 | 不支持 | 支持 |
| 可为null | 必须显式声明可空 | 默认可为null |
| 赋值行为 | 复制整个值 | 复制引用 |
| 大小限制 | 建议小于16字节 | 无限制 |
7.3 结构体的构造函数
public struct Rectangle
{
public double Width;
public double Height;
// 带参数的构造函数
public Rectangle(double width, double height)
{
Width = width;
Height = height;
}
// 编译错误:不能定义无参构造函数
// public Rectangle() { }
// 计算属性
public double Area => Width * Height;
}
// 使用
var rect1 = new Rectangle(10, 20); // 使用构造函数
var rect2 = new Rectangle(); // 使用默认构造函数,数值类型初始化为0
7.4 结构体的方法
public struct Vector3D
{
public double X, Y, Z;
public double Magnitude() => Math.Sqrt(X * X + Y * Y + Z * Z);
public override string ToString() => $"({X}, {Y}, {Z})";
public static Vector3D operator +(Vector3D a, Vector3D b) =>
new Vector3D { X = a.X + b.X, Y = a.Y + b.Y, Z = a.Z + b.Z };
}
7.5 只读结构体(C# 7.2+)
public readonly struct ImmutablePoint
{
public readonly int X;
public readonly int Y;
public ImmutablePoint(int x, int y)
{
X = x;
Y = y;
}
public double Distance => Math.Sqrt(X * X + Y * Y);
// 编译错误:不能修改只读结构体的字段
// public void Move(int dx, int dy) { X += dx; Y += dy; }
}
// 使用
var point = new ImmutablePoint(3, 4);
Console.WriteLine(point.Distance);
7.6 ref结构体(C# 7.2+)
public ref struct StackOnlyStruct
{
public int Value;
public void Increment() => Value++;
}
// 使用
var stackOnly = new StackOnlyStruct { Value = 10 };
stackOnly.Increment();
// 不能将ref结构体装箱或放入堆中
// object obj = stackOnly; // 编译错误
// List<StackOnlyStruct> list; // 编译错误
8.记录
记录(Record)是C# 9.0引入的一种新的引用类型,主要用于简化不可变数据模型的创建。
8.1 基本概念
- 不可变性:默认属性为init-only
- 值语义相等:基于内容而非引用比较
- 简洁语法:减少样板代码
- 非破坏性修改:with表达式创建修改后的副本
- ToString格式化:自动生成友好的ToString实现
// 位置记录(简洁语法)
public record Person(string FirstName, string LastName);
// 标准记录语法
public record Person
{
public string FirstName { get; init; }
public string LastName { get; init; }
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
}
8.2 记录类型与类的区别
| 特性 | 记录类型 | 类 |
|---|---|---|
| 相等性比较 | 基于内容(值语义) | 基于引用(默认) |
| 不可变性 | 默认 | 需要手动实现 |
| 复制修改 | 内置with表达式支持 | 需要手动实现 |
| ToString实现 | 自动生成友好格式 | 默认返回类型名称 |
| 继承 | 支持 | 支持 |
| 解构 | 内置支持 | 需要手动实现 |
8.3 记录类型的主要特性
1).位置记录
public record Point(double X, double Y);
// 使用
var p1 = new Point(1.0, 2.0);
var (x, y) = p1; // 解构
Console.WriteLine(p1); // 输出: Point { X = 1, Y = 2 }
2).非破坏性修改(with表达式)
var original = new Point(1.0, 2.0);
var modified = original with { X = 3.0 };
Console.WriteLine(original); // Point { X = 1, Y = 2 }
Console.WriteLine(modified); // Point { X = 3, Y = 2 }
3).值语义相等
var p1 = new Point(1.0, 2.0);
var p2 = new Point(1.0, 2.0);
Console.WriteLine(p1 == p2); // True
Console.WriteLine(ReferenceEquals(p1, p2)); // False
4).记录继承
public record Person(string FirstName, string LastName);
public record Student(string FirstName, string LastName, int Grade)
: Person(FirstName, LastName);
// 使用
var student = new Student("John", "Doe", 3);
Console.WriteLine(student); // Student { FirstName = John, LastName = Doe, Grade = 3 }
9.元组
元组是C#中的一种轻量级数据结构,用于临时组合多个值而无需创建专门的类或结构体。
// 未命名元组(Item1, Item2...)
var unnamedTuple = ("John", 30);
// 命名元组
var namedTuple = (Name: "John", Age: 30);
// 访问元素
Console.WriteLine(unnamedTuple.Item1); // "John"
Console.WriteLine(namedTuple.Age); // 30
// 显式声明元组类型
(string, int) person1 = ("Alice", 25);
(string Name, int Age) person2 = ("Bob", 30);
// 作为方法返回类型
public (string, int) GetPerson() => ("Charlie", 35);
10.枚举
10.1基本使用
枚举(Enumeration)是C#中的一种值类型,用于定义一组命名常量。
// 基本枚举
public enum Weekday
{
Monday, // 默认值0
Tuesday, // 1
Wednesday, // 2
Thursday, // 3
Friday, // 4
Saturday, // 5
Sunday // 6
}
// 带显式值的枚举
public enum StatusCode : int
{
Success = 200,
BadRequest = 400,
Unauthorized = 401,
NotFound = 404,
ServerError = 500
}
// ==========================
// 声明
public enum Color { Red, Green, Blue }
// 使用
Color favorite = Color.Blue;
// 比较
if (favorite == Color.Blue)
{
Console.WriteLine("蓝色");
}
10.2 标志枚举(Flags)
[Flags]
public enum Permissions
{
None = 0, // 0b_0000
Read = 1, // 0b_0001
Write = 2, // 0b_0010
Execute = 4, // 0b_0100
Delete = 8, // 0b_1000
All = Read | Write | Execute | Delete // 0b_1111 (15)
}
// 组合权限
Permissions userPermissions = Permissions.Read | Permissions.Write;
// 检查权限
if (userPermissions.HasFlag(Permissions.Read))
{
Console.WriteLine("拥有读取权限");
}
// 添加权限
userPermissions |= Permissions.Execute;
// 移除权限
userPermissions &= ~Permissions.Write;
// 检查多个权限
if ((userPermissions & (Permissions.Read | Permissions.Write)) == (Permissions.Read | Permissions.Write))
{
Console.WriteLine("拥有读取和写入权限");
}
10.3 枚举描述特性
public enum LogLevel
{
[Description("调试信息")]
Debug,
[Description("一般信息")]
Info,
[Description("警告信息")]
Warning,
[Description("错误信息")]
Error
}
public static string GetDescription(this Enum value)
{
var field = value.GetType().GetField(value.ToString());
var attribute = field?.GetCustomAttributes(typeof(DescriptionAttribute), false)
.FirstOrDefault() as DescriptionAttribute;
return attribute?.Description ?? value.ToString();
}
// 使用
Console.WriteLine(LogLevel.Warning.GetDescription()); // "警告信息"
10.4 枚举遍历
foreach (Weekday day in Enum.GetValues(typeof(Weekday)))
{
Console.WriteLine($"{day} = {(int)day}");
}
// 获取所有枚举名称
string[] colorNames = Enum.GetNames(typeof(Color));