C# 概述

C#发音为 “C-Sharp”是一种现代的面向对象的类型安全的编程语言由微软公司开发并于2000年首次发布C# 是在.NET框架上运行的后来随着 .NET Core 和 .NET 5+ 的推出它也支持跨平台开发C# 结合了 C 和 C++ 的强大功能以及 Java 的一些特性旨在提高程序员的生产力

⭐⭐特点

面向对象C# 支持封装继承和多态等面向对象的编程特性


类型安全C# 防止类型不匹配错误确保程序中的数据类型在编译时被正确检查
垃圾回收C# 自动管理内存通过垃圾回收机制自动释放不再使用的对象简化了内存管理
简单性与可读性C# 的语法设计简洁易于学习和阅读
强大的类库.NET Framework 提供了丰富的类库包括文件处理数据库访问网络通信等功能
多线程C# 支持多线程编程可以编写高效并行的代码
LINQC# 引入了 LINQLanguage Integrated Query使得查询数据源变得像 SQL 查询一样简单
异步编程C# 4.5 引入了 async 和 await 关键字使得异步编程更加简洁
动态类型从 C# 4.0 开始支持动态类型的变量允许更灵活的编程风格
模式匹配C# 7.0 引入了模式匹配使代码更简洁且功能强大
可空引用类型C# 8.0 加入了对可空引用类型的支持帮助开发者更好地避免空引用异常

⭐⭐应用领域

Web 开发使用 ASP.NET Core 构建 Web 应用和 RESTful API


桌面应用创建 Windows 桌面应用程序
游戏开发Unity 游戏引擎广泛使用 C#
移动应用使用 Xamarin 进行跨平台移动应用开发
物联网 (IoT)适用于各种 IoT 设备的软件开发
数据分析和科学计算虽然不是主要领域但 C# 也可以用于数据分析和科学计算项目

.NET框架

.NET框架简介

.NET是微软开发的一个软件框架和平台它旨在提供一个统一的编程模型和运行环境使得开发者能够创建各种类型的应用程序包括Web应用桌面应用移动应用游戏微服务架构等

⭐⭐.NET的核心特性包括

通用语言运行时Common Language Runtime, CLRCLR是.NET框架的执行引擎负责在应用程序运行时提供内存管理垃圾回收线程管理和异常处理等服务CLR还提供了类型安全检查和代码验证以确保代码的稳定性和安全性


框架类库Framework Class Library, FCL它是.NET Framework中的一系列类库的总称包含了大量预定义的类和接口这些类和接口提供了各种各样的功能从基本的数据类型操作到复杂的网络通信和数据库交互 基础类库Base Class Library, BCLBCL是.NET框架中的一系列预编译的类库它们为开发者提供了丰富的API涵盖了文件I/O网络通信数据处理图形绘制等众多领域极大地简化了开发过程
语言互操作性.NET支持多种编程语言包括C#Visual BasicF#等并允许这些语言之间的互操作即可以在同一项目中混合使用不同语言编写的代码
跨平台支持虽然最初的.NET框架主要针对Windows平台但后来推出的.NET Core和.NET 5/6/7等版本已经实现了跨平台能力可以在LinuxmacOS等操作系统上运行
开源和社区驱动.NET Core和后续版本是开源的这吸引了大量的开发者参与其中形成了活跃的社区生态不断推动着.NET的发展和创新
工具与IDE微软提供了Visual Studio作为.NET的主要集成开发环境IDE它拥有强大的编辑调试构建等功能此外还有许多其他IDE和文本编辑器也支持.NET开发如Visual Studio CodeRider等
高性能和现代化.NET不断优化性能支持现代开发模式如异步编程依赖注入微服务架构等同时提供了对云原生容器化等技术的支持
ASP.NETASP.NET是.NET框架中的Web应用开发框架提供了MVCWeb API等多种Web开发模型以及信号RSignalR等实时通信功能

💗💗.NET自2002年首次发布以来经历了多个版本的迭代从.NET Framework到.NET Core再到统一的.NET 5/6/7持续地改进和扩展其功能成为了现代软件开发的重要平台之一

.NET框架的名词缩写

  • 公共中间语言CILCommon Intermediate LanguageILIntermediate LanguageMSILMicrosoft Intermediate Language
  • 公共语言运行库CLRCommon Language Runtime
    • 基类库BCLBase Class Library
    • 垃圾收集GCGarbage Collection
    • 实时编译器JITJust-In-Time
  • 公共语言基础结构CLICommon Language Infrastructure
    • 公共类型系统CTSCommon Type System
    • 公共语言规范CLSCommon Language Specification

.NET框架的组成

graph TB;
A[IDE集成开发环境] --> B[MSIL编译的中间语言];
A --> C[FCL类库]
B --> D[CLR转换对应平台可执行的代码];
D --> C
D --> F[应用程序执行]

G[
    1. 开发者使用IDEVisual Studio编写源代码使用CC++C#等语言

    2. 源代码通过编译器被转换成中间语言MSIL
    
    3. 中间语言MSIL由公共语言运行时CLR处理
    
    4. JIT编译器将MSIL转换为本机代码
    
    5. CLR提供托管执行环境包括垃圾回收类型安全等服务
    
    6. 本机代码在硬件上运行
    
    7. 应用程序通过框架类库FCL使用各种服务和功能
    
    8. 最终应用程序执行并产生输出
]:::custom-style

classDef custom-style fill:#f9f,stroke:#333,stroke-width:4px;text-align: left;

VisualStudio

下载安装

💗💗 微软提供了 Visual Studio 作为 .NET 的主要集成开发环境IDE

🔗🔗 Visual Studio 安装文档

创建项目

  1. 点击创建新项目
  2. 选择C#控制台应用 → 下一步
  3. 项目名称HelloWorld
  4. 位置D:\VisualStudio Files\Project\
  5. 解决方案名称Day01
  6. 下一步 -> 创建

💗💗解决方案是一个或多个项目的集合它们通常代表了一个较大的软件系统或一组相关联的应用程序

💗💗项目是构成解决方案的基本单元代表了软件开发的一个具体目标比如一个类库一个控制台应用程序一个Windows服务或者一个Web应用

编程基础

基本结构

控制台应用程序的基本结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication {
class Program {
static void Main(string[] args) {
// 将文本输出到屏幕
Console.WriteLine("C#编程入门的第一个应用程序!");
Console.ReadKey();
}
}
}

关键字介绍

1
2
3
4
5
using               引入命名空间引入后可以使用该命名空间中已经写好的方法
namespace 命名空间ConsoleApplication命名空间的名称
{} 花括号作用域有效范围
classProgram类的名称
Main 程序入口函数程序从这个函数开始运行

注意事项

  1. 程序语句要用英文书写注释字符串随意
  2. 字符严格区分大小写

代码注释

单行注释

1
2
3
4
5
6
7
namespace ConsoleApplication { 
class Program {
static void Main(string[] args) {
// 这是单行注释
}
}
}

多行注释

1
2
3
4
5
6
7
8
9
namespace ConsoleApplication { 
class Program {
static void Main(string[] args) {
/* 这是多行...

... 注释! */
}
}
}

变量&常量

变量

⭐⭐变量值可以改变初始化的时候不需要赋值

  • 变量是用于存储数据的命名存储位置
  • 变量具有类型名称和值
    • 类型决定了变量可以存储的数据种类
    • 名称则用来标识这个特定的变量
    • 值是变量所保存的实际信息
1
2
//定义一个int类型的变量不赋值
int a;

常量

⭐⭐常量值不可以改变初始化的时候需要赋值

1
2
3
//定义一个int类型的常量
//const 用于定义常量的关键字
const int a = 1;

命名规范

  • 变量名必须以字母(大小写均可)或下划线开头
  • 变量名只能包含字母(大小写)数字下划线
  • 变量名不能使用关键字
  • 变量名不能重复
  • 变量名尽量有意义

💗💗常用命名规范

  • 变量可以由多个单词组成
  • 帕斯卡命名法变量可以由多个单词组成每个单词首字母大写通常用于类型名和方法名StudentName
  • 驼峰命名法变量第一个单词首字母小写其他单词首字母大写通常用于变量名studentName
  • 匈牙利命名法首字母使用小写数据类型标识符其他使用帕斯卡命名sStudentName
  • 全大写命名法单词全部大写通常用于常量和单词缩写SN
  • 下划线连接命名法变量可以由多个单词组成单词之间用下划线连接通常用于常量STUDENT_NAME

数值类型

整数类型

💗💗无符号意味着它只表示正数大于0的数

💗💗有符号意味着它既可以表示正数也可以表示负数和0

  • byte8位无符号整数
  • sbyte8位有符号整数
  • short 或 Int1616位有符号整数
  • ushort 或 UInt1616位无符号整数
  • int 或 Int3232位有符号整数
  • uint 或 UInt3232位无符号整数
  • long 或 Int6464位有符号整数
  • ulong 或 UInt6464位无符号整数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 8位无符号整数
byte byteVar = 50;
// 8位有符号整数
sbyte sbyteVar = -50;

// 16位有符号整数
short shortVar = -32767;
// 16位无符号整数
ushort ushortVar = 65535;

// 32位有符号整数
int intVar = -2147483647;
// 32位无符号整数
uint uintVar = 4294967295u; // 注意'u'后缀表明这是一个无符号整数

// 64位有符号整数
long longVar = -9223372036854775807L; // 注意'L'后缀表明这是一个长整型
// 64位无符号整数
ulong ulongVar = 18446744073709551615UL; // 注意'UL'后缀表明这是一个无符号长整型

浮点数类型

  • float32位单精度浮点数
  • double64位双精度浮点数
  • decimal128位十进制浮点数用于货币计算
1
2
3
4
5
6
7
8
// 32位单精度浮点数
float floatVar = 3.14159f; // 使用 'f' 或 'F' 后缀来明确指出这是 float 类型

// 64位双精度浮点数
double doubleVar = 3.14159; // 如果没有指定后缀默认为 double 类型

// 128位十进制浮点数用于货币计算
decimal decimalVar = 123.456m; // 使用 'm' 或 'M' 后缀来明确指出这是 decimal 类型

非数值类型

  • 字符类型char
  • 字符串类型string 多个字符串可以用 + 号连接
  • 布尔类型bool 布尔值可以是true或false
1
2
3
4
5
6
7
8
9
10
11
// 字符类型: char
char ch = 'A'; // 必须使用单引号包围单个字符

// 字符串类型: string
string str = "Hello, world"; // 使用双引号包围字符串

// 字符串连接
string fullName = str + " " + "!";

// 布尔类型: bool
bool isTrue = true; // 或者 false

类型转换

隐式转换

隐式转换发生在当一个较小的类型可以安全地转换为较大的类型时例如从 short 到 int从 int 到 long从 short 到 long

1
2
3
short smallNumber = 100;
int iNumber = smallNumber;
long lNumber = iNumber;

显式转换

显式转换需要你明确地将一个类型转换为另一个类型这通常涉及到可能的数据损失例如从 double 到 int

1
2
3
double doubleValue = 123.456;
//显式转换小数部分将被截断intValue的值为123
int intValue = (int) doubleValue;

使用 Convert 类

💗💗 System 命名空间下的 Convert 类提供了多种静态方法来转换不同类型的数据

1
2
3
//字符串类型的 123 转数值 int 型的 123
string strNum = "123";
int num = Convert.ToInt32(strNum);

使用 Parse 和 TryParse 方法

💗💗 如 int, double, float, 和 decimal 等类型提供了 Parse 和 TryParse 方法来进行转换

1
2
3
4
5
6
string strNum = "123.45";
double num = double.Parse(strNum);

string strNum2 = "这是字符串";
double num2;
bool success = double.TryParse(strNum2, out num2); // 返回 false

使用 as 关键字和类型转换运算符

1
2
3
4
5
object obj = new int[] { 1, 2, 3 };
int[] arr = obj as int[]; // 如果obj不是int[]arr将为null

object obj2 = "Hello";
string str = (string)obj2; // 如果obj2不是string将抛出异常

使用 checked 和 unchecked 关键字

1
2
3
4
5
6
7
8
9
10
11
checked
{
int a = int.MaxValue;
int b = a + 1; // 抛出 OverflowException
}

unchecked
{
int a = int.MaxValue;
int b = a + 1; // 不会抛出异常b将得到溢出后的结果
}

运算符

数学运算符

运 算 符示例表达式结 果
+ var1 = var2 + var3; var1 的值是 var2 与 var3 的和
- var1 = var2 - var3; var1 的值是 var2 减去 var3 所得的值
* var1 = var2 * var3; var1 的值是 var2 与 var3 的乘积
/ var1 = var2 / var3; var1 是 var2 除以 var3 所得的值
% var1 = var2 % var3; var1 是 var2 除以 var3 所得的余数
++ var1 = ++var2; var1 的值是 var2 + 1var2 递增 1
-- var1 = --var2; var1 的值是 var2 - 1var2 递减 1
++ var1 = var2++; var1 的值是 var2var2 递增 1
-- var1 = var2--; var1 的值是 var2var2 递减 1

💗💗 ++ 在前边自身先加 1再执行赋值操作++ 在后边先执行赋值操作自身再加 1

💗💗 -- 在前边自身先减 1再执行赋值操作-- 在后边先执行赋值操作自身再减 1

赋值运算符

💗💗可以理解为上边数学运算的缩写形式

运 算 符示例表达式结 果
= var1 = var2; var1 被赋予 var2 的值
+= var1 += var2; var1 被赋予 var1 与 var2 的和
-= var1 -= var2; var1 被赋予 var1 与 var2 的差
*= var1 *= var2; var1 被赋予 var1 与 var2 的乘积
/= var1 /= var2; var1 被赋予 var1 与 var2 相除所得的结果
%= var1 %= var2; var1 被赋予 var1 与 var2 相除所得的余数

关系运算符

运 算 符 示例表达式 结 果
== var1 = var2 == var3; 如果 var2 等于 var3var1 的值就是 true否则为 false
!= var1 = var2 != var3; 如果 var2 不等于 var3var1 的值就是 true否则为 false
< var1 = var2 < var3; 如果 var2 小于 var3var1 的值就是 true否则为 false
> var1 = var2 > var3; 如果 var2 大于 var3var1 的值就是 true否则为 false
<= var1 = var2 <= var3; 如果 var2 小于等于 var3var1 的值就是 true否则为 false
>= var1 = var2 >= var3; 如果 var2 大于等于 var3var1 的值就是 true否则为 false

逻辑运算符

运 算 符 示例表达式 结 果
&& var1 = var2 && var3; 如果 var2 和 var3 都是 truevar1 的值就是 true否则为 false(逻辑与)
|| var1 = var2 || var3; 如果 var2 或 var3 是 true(或两者都是)var1 的值就是 true否则为 false(逻辑或)
! var1 = false; var2 = !var1; 如果 var1 是false那么 var2 的值就是true

💗💗 a && b 只要 a 或者 b 有一个是 false, 那么 a && b 的结果就是 false只有 a 和 b 都是 true, 那么 a && b 的结果才是 true

💗💗 a || b 只要 a 或者 b 有一个是 true, 那么 a || b 的结果就是 true只有 a 和 b 都是 false, 那么 a || b 的结果才是 false

a b a && b 的结果 a || b 的结果
true true true true
false true false true
false true false true
false false false false

运算符优先级

💗💗优先级由高到低

优 先 级 运 算 符 描 述
1 () 组合函数调用
[] 数组索引
() 组合括号
2 + 正号
- 负号
! 逻辑非
~ 按位非
++ 前缀递增
-- 前缀递减
3 * 乘法
/ 除法
% 取模
4 + 加法
- 减法
5 < 小于
> 大于
<= 小于等于
>= 大于等于
6 == 相等
!= 不相等
7 && 逻辑与
|| 逻辑或
! 逻辑非
8 ?: 条件三元运算符
9 = 简单赋值
+= 加法赋值
-= 减法赋值
*= 乘法赋值
/= 除法赋值
%= 取模赋值

字符串

string 类是不可变的immutable这意味着一旦一个字符串被创建它的值就不能改变所有的字符串操作都会生成新的字符串实例

常用方法

方法 描 述
Length 返回字符串中的字符数
Chars 通过索引访问字符串中的单个字符
Concat 连接两个或多个字符串并返回一个新的字符串
Compare 比较两个字符串
CompareTo 比较当前字符串与另一个字符串
Contains 检查字符串是否包含指定的子字符串
Copy 返回当前字符串的一个副本
Equals 比较当前字符串与另一个字符串是否相等
IndexOf 返回指定字符或子字符串首次出现的位置
LastIndexOf 返回指定字符或子字符串最后一次出现的位置
Insert 在当前字符串中插入一个新子字符串并返回一个新的字符串
PadLeft 返回一个新的字符串在当前字符串左侧填充指定字符
PadRight 返回一个新的字符串在当前字符串右侧填充指定字符
Remove 移除指定位置的字符或子字符串并返回一个新的字符串
Replace 替换当前字符串中的所有指定字符或子字符串并返回一个新的字符串
StartsWith 检查当前字符串是否以指定的子字符串开头
EndsWith 检查当前字符串是否以指定的子字符串结尾
Substring 返回从指定位置开始的新子字符串
ToLower 将当前字符串转换为小写形式
ToUpper 将当前字符串转换为大写形式
Trim 去掉字符串两端的空白字符
TrimStart 去掉字符串开头的空白字符或指定字符
TrimEnd 去掉字符串结尾的空白字符或指定字符
Split 将当前字符串分割成子字符串数组
Join 将一个字符串数组合并成一个字符串
Format 使用指定格式字符串和参数数组来格式化字符串
IsNullOrEmpty 检查一个字符串是否为 null 或空
IsNullOrWhiteSpace 检查一个字符串是否为 null 或只包含空白字符
GetHashCode 返回当前字符串的哈希码
GetBytes 将字符串转换为字节数组
GetChars 返回一个字符数组该数组包含字符串中的字符
ToCharArray 返回一个包含字符串中字符的字符数组
Clone 返回当前对象的一个精确副本

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
using System;

public class StringMethodsExample
{
public static void Main()
{
// 基础示例
string str = "Hello, world!";
string str2 = " Goodbye, world! ";

// Length 属性
Console.WriteLine($"Length: {str.Length}");

// Chars 属性
Console.WriteLine($"First character: {str[0]}");

// Concat 方法
string concatenated = string.Concat(str, " ", str2.Trim());
Console.WriteLine($"Concatenated: {concatenated}");

// Compare 方法
int compareResult = string.Compare(str, str2, true);
Console.WriteLine($"Comparison (ignore case): {compareResult}");

// CompareTo 方法
int compareToResult = str.CompareTo(str2);
Console.WriteLine($"CompareTo result: {compareToResult}");

// Contains 方法
bool containsWorld = str.Contains("world");
Console.WriteLine($"Contains 'world': {containsWorld}");

// Copy 方法
string copy = str.Copy();
Console.WriteLine($"Copied: {copy}");

// Equals 方法
bool equalsResult = str.Equals(str2, StringComparison.OrdinalIgnoreCase);
Console.WriteLine($"Equals (ignore case): {equalsResult}");

// IndexOf 方法
int index = str.IndexOf("world");
Console.WriteLine($"Index of 'world': {index}");

// LastIndexOf 方法
int lastIndex = str.LastIndexOf("o");
Console.WriteLine($"Last index of 'o': {lastIndex}");

// Insert 方法
string inserted = str.Insert(7, "beautiful ");
Console.WriteLine($"Inserted: {inserted}");

// PadLeft 方法
string paddedLeft = str.PadLeft(20, '*');
Console.WriteLine($"Padded Left: {paddedLeft}");

// PadRight 方法
string paddedRight = str.PadRight(20, '*');
Console.WriteLine($"Padded Right: {paddedRight}");

// Remove 方法
string removed = str.Remove(7, 7);
Console.WriteLine($"Removed: {removed}");

// Replace 方法
string replaced = str.Replace("world", "C#");
Console.WriteLine($"Replaced: {replaced}");

// StartsWith 方法
bool startsWithHello = str.StartsWith("Hello");
Console.WriteLine($"StartsWith 'Hello': {startsWithHello}");

// EndsWith 方法
bool endsWithExclamation = str.EndsWith("!");
Console.WriteLine($"EndsWith '!': {endsWithExclamation}");

// Substring 方法
string substring = str.Substring(7, 5);
Console.WriteLine($"Substring: {substring}");

// ToLower 方法
string lowercase = str.ToLower();
Console.WriteLine($"Lowercase: {lowercase}");

// ToUpper 方法
string uppercase = str.ToUpper();
Console.WriteLine($"Uppercase: {uppercase}");

// Trim 方法
string trimmed = str2.Trim();
Console.WriteLine($"Trimmed: '{trimmed}'");

// TrimStart 方法
string trimmedStart = str2.TrimStart();
Console.WriteLine($"Trimmed Start: '{trimmedStart}'");

// TrimEnd 方法
string trimmedEnd = str2.TrimEnd();
Console.WriteLine($"Trimmed End: '{trimmedEnd}'");

// Split 方法
string[] splitArray = str.Split(',');
foreach (var item in splitArray)
{
Console.WriteLine($"Split item: {item}");
}

// Join 方法
string joined = string.Join("-", splitArray);
Console.WriteLine($"Joined: {joined}");

// Format 方法
string formatted = string.Format("Message: {0}, Time: {1}", str, DateTime.Now);
Console.WriteLine($"Formatted: {formatted}");

// IsNullOrEmpty 方法
string nullOrEmpty = string.IsNullOrEmpty(null);
Console.WriteLine($"IsNullOrEmpty (null): {nullOrEmpty}");

// IsNullOrWhiteSpace 方法
bool isNullOrWhiteSpace = string.IsNullOrWhiteSpace(" ");
Console.WriteLine($"IsNullOrWhiteSpace (' '): {isNullOrWhiteSpace}");

// GetHashCode 方法
int hashCode = str.GetHashCode();
Console.WriteLine($"Hash code: {hashCode}");

// GetBytes 方法
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(str);
Console.WriteLine($"Bytes: {BitConverter.ToString(bytes)}");

// GetChars 方法
char[] chars = str.ToCharArray();
Console.WriteLine($"Chars: {string.Join(", ", chars)}");

// Clone 方法
string cloned = (string)str.Clone();
Console.WriteLine($"Cloned: {cloned}");
}
}

分支语句

if 语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//控制台输入一个值
int var1 = int.Parse(Console.ReadLine());
if (var1 == 1)
{
// 等于 1
}
else if (var1 == 2)
{
// 等于 2
}
else if (var1 == 3 || var1 == 4)
{
// 等于 3 或者 等于 4
}
else
{
// 其他的值
}

switch 语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//控制台输入一个数输出星期几
int myInteger = int.Parse(Console.ReadLine());
string dayOfWeek;
switch (myInteger)
{
case 1:
dayOfWeek = "星期一";
break;
case 2:
dayOfWeek = "星期二";
break;
case 3:
dayOfWeek = "星期三";
break;
case 4:
dayOfWeek = "星期四";
break;
case 5:
dayOfWeek = "星期五";
break;
case 6:
dayOfWeek = "星期六";
break;
case 7:
dayOfWeek = "星期日";
break;
default:
// 其他的值
dayOfWeek = "日期错误";
break;
}
Console.WriteLine(dayOfWeek);

循环语句

while 循环语句

⭐⭐ while 循环会在 条件是对的时候 重复执行 while{ } 代码块里边的代码循环一次就判断一次条件是否成立直到不成立的时候退出

1
2
3
4
5
6
//当 i = 5 时while 条件 i < 5 不成立循环结束
int i = 0;
while (i < 5) {
Console.WriteLine("Count is: " + i);
i = i + 1;
}

do…while 循环语句

⭐⭐与 while 循环类似但 do…while 循环至少会执行一次先执行 do{ } 代码块里边的代码在进行判断条件是否成立成立的话继续循环不成立就结束循环

1
2
3
4
5
int i = 0;
do {
Console.WriteLine("Count is: " + i);
i++;
} while (i < 5);

for 循环语句

⭐⭐ for 循环是最常用的循环结构之一它允许你在循环开始之前初始化一个或多个变量并且可以在每次迭代后更新这些变量

1
2
3
4
//当 i = 5 时while 条件 i < 5 不成立循环结束
for (int i = 0; i < 5; i++) {
Console.WriteLine("Count is: " + i);
}

foreach 循环语句

⭐⭐ foreach 循环用于遍历数组集合等数据结构中的元素它提供了一种更简洁的方式来访问集合中的每个元素

1
2
3
4
string[] strs = { "a", "b", "c", "d", "e" };
foreach (string str in strs) {
Console.WriteLine(str);
}

控制循环的关键字

⭐⭐ break: 立即退出循环

⭐⭐ continue: 跳过当前迭代的剩余部分并继续到下一次迭代

1
2
3
4
5
6
7
8
9
for (int i = 0; i < 10; i++) {
if (i == 5) {
break; // 退出循环
}
if (i % 2 == 0) {
continue; // 跳过偶数
}
Console.WriteLine(i); // 输出奇数 1, 3
}

数组

数组是一种基本的数据结构用于存储固定大小的同类型元素集合

一维数组

1
2
3
4
5
6
7
8
// 声明数组并指定大小为5
int[] numbers = new int[5];

//初始化数据写法一
int[] numbers = new int[5] { 1, 2, 3, 4, 5 };

//初始化数据写法二
int[] numbers = { 1, 2, 3, 4, 5 };

多维数组

多维数组允许你存储多个维度的数据最常见的多维数组是二维数组可以想象成表格的形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 声明数组创建一个3行4列的数组
int[][] matrix = new int[3, 4];

//初始化数据写法一
int[][] matrix = new int[3, 4] {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 }
};

//初始化数据写法二
int[][] matrix = {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 }
};

交错数组

1
2
3
4
5
6
7
8
9
10
int[][] jaggedArray;
jaggedArray = new int[3][]; // 创建一个包含3个一维数组的交错数组
jaggedArray[0] = new int[3];
jaggedArray[1] = new int[2];
jaggedArray[2] = new int[1];

int[][] jaggedArray = new int[3][];
jaggedArray[0] = new int[] { 1, 2, 3 };
jaggedArray[1] = new int[] { 4, 5 };
jaggedArray[2] = new int[] { 6 };

获取数组元素

1
2
3
4
5
6
7
8
9
10
11
12
// 一维数组访问数组元素
int[] array = { 1, 2, 3, 4, 5 };
int firstElement = array[0]; // 访问第一个元素
int lastElement = array[array.Length - 1]; // 访问最后一个元素

// 多维数组访问数组元素
int[][] matrix = new int[3, 4] {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 }
};
int element = matrix[1, 2]; // 访问第二行第三列的元素

遍历数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 遍历一维数组
int[] array = { 1, 2, 3, 4, 5 };
for (int i = 0; i < array.Length; i++)
{
Console.WriteLine(array[i]);
}

// 遍历多维数组
int[][] matrix = new int[3, 4] {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 }
};
for (int row = 0; row < matrix.GetLength(0); row++)
{
for (int col = 0; col < matrix.GetLength(1); col++)
{
Console.Write(matrix[row, col] + " ");
}
Console.WriteLine();
}

数组排序

1
2
int[] array = { 2, 1, 4, 3, 5 };
Array.Sort(array);

数组元素查找

1
2
3
int[] array = { 2, 1, 4, 3, 5 };
// 查找特定元素的位置返回对应的下标
int index = Array.IndexOf(array, 3);

函数

函数通常被称为方法方法是程序的基本构建块之一用于执行特定任务一个方法的声明包括访问修饰符返回类型方法名和参数列表

语法

1
2
3
4
[access_modifier] [return_type] [method_name]([parameter_list])
{
// 方法体
}
  • access_modifier访问修饰符如 public, private, protected决定了方法的可见性
  • return_type返回类型定义了方法执行后返回给调用者的数据类型如果方法不返回任何值则使用 void
  • method_name方法名遵循C#的命名规则通常首字母大写
  • parameter_list参数列表包含零个或多个参数每个参数都有其类型和名称

⭐⭐举例

1
2
3
4
5
6
7
//这个方法的含义是传入 x 和 y 两个参数返回 x 和 y 的和
public int Add(int x, int y)
{
return x + y;
}
//方法调用
int sum = Add(1, 2);

方法重载

方法可以通过使用相同的名称但不同的参数列表来实现重载这使得同一个方法名称可以有不同的行为

1
2
3
4
5
6
7
8
9
10
11
//这个方法的含义是传入 x 和 y 两个参数int类型返回 x 和 y 的和
public int Add(int x, int y)
{
return x + y;
}

//这个方法的含义是传入 x 和 y 两个参数double类型返回 x 和 y 的和
public double Add(double x, double y)
{
return x + y;
}

可选参数

方法可以接受可选参数这些参数在调用时可以省略

1
2
3
4
public void Greet(string name, string greeting = "Hello")
{
Console.WriteLine($"{greeting}, {name}!");
}

参数数组

参数数组允许方法接收任意数量的相同类型的参数

1
2
3
4
5
6
7
8
9
10
11
12
//这个方法的含义是计算传入数值的和
public int Sum(params int[] numbers)
{
int sum = 0;
foreach (int number in numbers)
{
sum += number;
}
return sum;
}
//方法调用
int sum = Add(1, 2, 3, 4);

局部函数

局部函数是在另一个方法体内定义的方法它们只能在定义它们的方法内部被调用

1
2
3
4
5
6
7
8
9
10
11
12
public void Calculate()
{
int result;

void ComputeResult()
{
result = 10 * 10;
}

ComputeResult();
Console.WriteLine(result);
}

匿名方法

匿名方法是一种没有名称的方法它主要用于作为委托的实例匿名方法是在C# 2.0中引入的一个特性它允许你在不显式定义方法名称的情况下编写方法体匿名方法通常用于一次性使用的简单操作或者当你需要传递一小段代码作为参数时

1
2
3
4
System.Action<int> printSquare = delegate(int x) {
Console.WriteLine("Square of {0} is {1}", x, x * x);
};
printSquare(5); // 调用匿名方法

Lambda表达式

Lambda表达式是C#中一种简洁的基于行的函数定义方式它允许你创建简洁的匿名函数Lambda表达式从C# 3.0开始引入并且在后续版本中得到了进一步的增强

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//传入一个数组根据传入的条件打印出数值
void FilterArray(int[] arr, Func<int, bool> filter)
{
foreach (var x in arr)
{
if (filter(x))
{
Console.WriteLine(item);
}
}
}

// 使用Lambda表达式传入数据打印出复符合条件为(x => x % 2 == 0)的数
FilterArray(new int[] { 1, 2, 3, 4, 5 }, x => x % 2 == 0);

委托

委托是一种引用类型的数据结构它允许你封装一个方法的引用并通过这个引用来调用方法委托可以看作是函数指针的一个面向对象版本但它们更加安全且具有更多的功能

定义委托

使用 delegate 关键字来定义一个委托类型指定返回类型和参数列表这些必须与预期要调用的方法相匹配

1
public delegate double MyDelegate(double param1, double param2);

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//定义一个乘法
double Multiply(double param1, double param2) {
return param1 * param2;
}

//定义一个除法
double Divide(double param1, double param2) {
return param1 / param2;
}

//定义一个委托
delegate double MyDelegate(double param1, double param2);

//第一种实例化方式初始化
MyDelegate d1 = new MyDelegate(Multiply);
d1(1, 2);//调用

//第二种实例化方式直接赋值
MyDelegate d2 = Divide;
d2(2, 4);//调用

匿名方法

1
2
3
4
5
6
7
8
//定义一个委托
delegate void MyDelegate(string param1);
//第三种实例化方式匿名方法
MyDelegate d = delegate (string msg) {
Console.WriteLine(msg);
};
//调用
d("Hello, World!");

Lambda 表达式

1
2
3
4
5
6
//定义一个委托
delegate void MyDelegate(string param1);
//第四种实例化方式Lambda 表达式
MyDelegate d = (msg) => Console.WriteLine(msg);
//调用
d("Hello, World!");

Action 委托

Action 是一个内置的泛型委托类型它主要用来封装那些返回类型为 void 的方法

Action 委托使得你可以更加简洁地使用委托因为你不必定义自己的委托类型这有助于提高代码的可读性和效率

⭐⭐ Action 委托的特点

Action 委托没有返回值即其返回类型为 void
Action 委托可以带有0到16个输入参数
Action 委托定义在 System 命名空间中

⭐⭐ Action 委托的定义如下

每个重载版本对应不同的参数数量例如Action 表示一个接受一个参数的 Action 委托

1
2
3
4
5
public delegate void Action();
public delegate void Action<T1>(T1 arg1);
public delegate void Action<T1, T2>(T1 arg1, T2 arg2);
// ...
// 最多定义到16个参数

⭐⭐使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using System;

class Program
{
static void Main()
{
// 定义一个不带参数的 Action 委托实例
Action sayHello = () => Console.WriteLine("Hello!");
// 调用委托
sayHello();

// 定义一个 Action 委托实例接受一个整数参数
Action<int> printNumber = number => Console.WriteLine(number);
// 调用委托
printNumber(42);
}
}

Func 委托

Func 是一个内置的泛型委托类型用于封装返回值的方法

Func 委托使得你可以更加简洁地使用委托因为你不必定义自己的委托类型这有助于提高代码的可读性和效率

⭐⭐ Func 委托的特点

Func 委托有一个指定类型的返回值
Func 委托可以带有0到16个输入参数
Func 委托定义在 System 命名空间中

⭐⭐ Func 委托的定义如下

每个重载版本对应不同的参数数量和返回类型例如Func<T1, TResult> 表示一个接受一个参数 T1 并返回类型为 TResult 的委托

1
2
3
4
5
public delegate TResult Func<TResult>();
public delegate TResult Func<T1, TResult>(T1 arg1);
public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2);
// ...
// 最多定义到16个参数

⭐⭐使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using System;

class Program
{
static void Main()
{
// 定义一个不带参数的 Func 委托实例
Func<string> getGreeting = () => "Hello!";

// 调用委托
Console.WriteLine(getGreeting()); // 输出: Hello!

// 定义一个 Func 委托实例接受一个整数参数并返回一个整数
Func<int, int> square = x => x * x;

// 调用委托
Console.WriteLine(square(5)); // 输出: 25

// 定义一个带有两个参数的 Func 委托实例
Func<int, int, bool> isEqual = (a, b) => a == b;

// 调用委托
Console.WriteLine(isEqual(10, 10)); // 输出: True
}
}

多播委托

多播委托是指一个委托实例可以关联多个方法的能力这意味着当你调用这个委托时所有关联的方法都会被依次调用这种特性在很多场合下都非常有用尤其是在事件处理中

⭐⭐多播委托的工作原理

多播委托是通过 += 和 -= 运算符来实现方法的添加和移除的

当一个方法被添加到委托时实际上是创建了一个包含多个方法调用的新委托

因此当原始委托被调用时实际上是在执行一系列的方法

⭐⭐注意事项

  • 顺序当调用多播委托时方法按照它们添加到委托中的顺序依次调用
  • 异常处理如果其中一个方法抛出异常那么后续的方法将不再被调用为了避免这种情况你需要捕获异常
  • 性能影响多播委托可能会对性能产生一定影响尤其是在关联了很多方法的情况下这是因为每次调用委托都需要创建一个新的委托链

⭐⭐使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
using System;

public class Program
{
public delegate void MyDelegate(string message);

public static void Main()
{
// 创建委托实例
MyDelegate myDel = new MyDelegate(SayHello);

// 添加另一个方法到委托实例
myDel += SayGoodbye;

// 调用委托
myDel("World");

// 移除方法
myDel -= SayGoodbye;

// 再次调用委托
myDel("World");
}

// 定义委托可以指向的方法
public static void SayHello(string msg)
{
Console.WriteLine("Hello " + msg);
}

public static void SayGoodbye(string msg)
{
Console.WriteLine("Goodbye " + msg);
}
}

委托与事件

委托常常与事件一起使用事件是基于委托的并且通常都是多播的这意味着你可以为同一个事件注册多个监听器当事件触发时所有注册的监听器都会被调用

⭐⭐事件的特点

  • 定义事件是一种特殊的委托它提供了发布/订阅机制
  • 作用事件通常用来通知其他对象发生了某些特定的动作
  • 语法使用 event 关键字定义事件后面跟着委托类型的名称

⭐⭐事件的特点

  • 事件是基于委托的但具有额外的安全性和封装性
  • 事件只能在类内部被触发而不能直接从类外部被调用
  • 事件的添加和移除通过 += 和 -= 运算符来完成
  • 事件的触发通常通过一个保护的虚方法来实现比如 OnMessageSent

⭐⭐使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
using System;

//定义了一个 Publisher 类它有一个 MessageReceived 事件
public class Publisher
{
//定义一个事件
public event EventHandler<MessageEventArgs> MessageReceived;

protected virtual void OnMessageReceived(string message)
{
MessageReceived?.Invoke(this, new MessageEventArgs(message));
}

public void SendMessage(string message)
{
OnMessageReceived(message);
}
}

//Subscriber 类订阅了这个事件并定义了一个事件处理方法 OnMessageReceived
public class Subscriber
{
public void Subscribe(Publisher publisher)
{
publisher.MessageReceived += OnMessageReceived;
}

private void OnMessageReceived(object sender, MessageEventArgs e)
{
Console.WriteLine($"Subscriber received: {e.Message}");
}
}

public class MessageEventArgs : EventArgs
{
public string Message { get; }

public MessageEventArgs(string message)
{
Message = message;
}
}

public class Program
{
public static void Main()
{
Publisher publisher = new Publisher();
Subscriber subscriber1 = new Subscriber();
Subscriber subscriber2 = new Subscriber();

subscriber1.Subscribe(publisher);
subscriber2.Subscribe(publisher);

//当 Publisher 发送消息时所有订阅的 Subscriber 实例都会收到消息
publisher.SendMessage("Hello World!");
}
}

常见用途

  • 事件处理委托通常用于实现事件处理机制事件本质上就是公开给其他对象订阅的委托
  • 异步编程委托广泛应用于异步编程模型如 BeginInvoke 和 EndInvoke 方法
  • 回调函数委托可以作为方法的参数传递用于实现回调机制
  • 命令模式在设计模式中委托可以用来实现命令模式

枚举

枚举enumeration是一种值类型它提供了一种方便的方式来定义一组命名的常量枚举非常适合用来表示一组有限的相关的值

声明枚举类

使用 enum 关键字来声明枚举类型

1
2
3
4
5
6
public enum Color
{
Red,
Green,
Blue
}

枚举成员的默认值

默认情况下第一个枚举成员的值是 0之后的每个成员的值依次递增 1如果需要指定一个不同的值可以在成员后面加上等号和相应的整数值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//默认情况
public enum Color
{
Red = 0,
Green = 1,
Blue = 2
}

//也可以自定义
public enum Color
{
Red = 1,
Green = 2,
Blue = 3
}

枚举的底层类型

枚举的底层类型默认是 int但你也可以指定其他整型如 byte, short, long 或 sbyte, ushort, ulong

1
2
3
4
5
6
public enum MyEnum : byte
{
Value1,
Value2,
Value3
}

获取枚举成员

1
Color c = Color.Red;

类型转换

1
2
3
4
5
6
7
8
9
//枚举成员转换为数值
int intValue = (int) Color.Red;
//数值转换为枚举成员
Color colorValue = (Color) 1;

//枚举成员转换为字符串
string name = Color.Red.ToString();
//字符串转换为枚举成员
Color color = (Color) Enum.Parse(typeof(Color), "Red");

枚举与 switch 语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void ProcessColor(Color color)
{
switch (color)
{
case Color.Red:
Console.WriteLine("红色");
break;
case Color.Green:
Console.WriteLine("绿色");
break;
case Color.Blue:
Console.WriteLine("蓝色");
break;
}
}

ProcessColor(Color.Blue);

使用标志

枚举也可以用作位标志这意味着它们可以用作二进制掩码要实现这一点可以使用 Flags 属性并且每个成员的值应该是 2 的幂次方

1
2
3
4
5
6
7
8
9
10
[Flags]
public enum Permissions
{
None = 0,
Read = 1,
Write = 2,
Execute = 4
}

Permissions p = Permissions.Read | Permissions.Write;

实用方法

1
2
3
4
//获取 Color 枚举类中所有枚举值
Color[] colors = (Color[]) Enum.GetValues(typeof(Color));
//获取 Color 枚举类中所有枚举成员名称
string[] colorNames = Enum.GetNames(typeof(Color));

结构体

结构体struct是一种值类型的数据结构用于封装一组相关的数据当它被赋值或传递给函数时会创建一个新的副本

  • 值类型结构体是值类型这意味着每个实例都是独立的对象对一个实例所做的更改不会影响到其他实例
  • 存储位置结构体存储在栈上因此它们在内存中的占用较小且访问速度快
  • 初始化结构体必须在定义时或构造函数中初始化所有字段
  • 不可继承性结构体不能从其他结构体或类派生也不能作为基类被继承
  • 默认值结构体字段有默认值如整数为 0浮点数为 0.0布尔值为 false 等
  • 不可变性虽然结构体可以包含可变成员但通常建议将结构体设计为不可变的以避免副作用

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//定义一个结构体
struct Point
{
public int X;
public int Y;

public Point(int x, int y)
{
X = x;
Y = y;
}
}

//使用示例
Point p1 = new Point(10, 20);
Point p2 = p1; // 复制一份 p1 到 p2
p2.X = 100; // 修改 p2 不会影响 p1
Console.WriteLine(p1.X); // 输出 10

常见用途

  • 简单的数据组合
  • 坐标点
  • 颜色
  • 用于需要高效内存访问和管理的场景等

面向对象

对象

Class是面向对象编程中的一个核心概念它是一种数据类型定义用于描述一组具有相同属性和行为的对象

⭐⭐ 访问修饰符

  • public可以被任何地方访问
  • private只能在同一个类中访问
  • protected可以在同一个类或派生类中访问
  • internal只能在同一程序集中访问
  • protected internal可以在同一程序集或派生类中访问
  • private protected只能在同一个类或派生类中访问
  • static表示静态成员不依赖于类的实例

⭐⭐ 字段与属性

  • 字段直接存储数据的变量
  • 属性提供了类似于公共字段的访问方式但实际上背后使用私有字段并允许你控制读写逻辑

⭐⭐ 方法

  • 方法定义了类的行为可以接受参数并返回值
  • 构造函数构造函数用于初始化类的新实例每个类至少有一个构造函数如果没有显式声明编译器会提供一个默认的无参构造函数
  • 析构函数析构函数用于释放非托管资源在对象生命周期结束时自动调用析构函数没有参数且不能被重载

⭐⭐ 静态类与成员

  • 静态类不能被实例化所有成员都必须是静态的
  • 静态成员与类本身关联而不是特定的实例

⭐⭐ 继承支持单一继承子类可以从父类继承成员

⭐⭐ 其他类

  • 抽象类可以包含抽象成员未实现的方法属性等用于作为基类供其他类继承
  • 密封类密封类不能被继承使用 sealed 关键字标记
  • 泛型类: 泛型类允许在定义类时指定类型参数从而提高代码复用性和类型安全性
  • 接口定义了一组方法属性索引器和事件但不包含实现

普通类

普通类是最常见的类形式它们可以包含各种成员如字段属性方法等并且可以被实例化可以通过使用 class 关键字来指定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Car
{
private string _model;
private int _year;

//构造函数
public Car(string model, int year)
{
_model = model;
_year = year;
}

// 属性包含 get 块 和 set 块
public string Model
{
get { return _model; }
set { _model = value; }
}

// 没有处理逻辑的话可以简写
public int Year
{
get;set;
}

public void Drive()
{
Console.WriteLine($"Driving the {_model} ({_year})");
}
}

⭐⭐ 继承

继承是一个重要的面向对象编程特性它允许创建新的类派生类或子类来继承现有类基类或父类的属性和方法通过继承可以重用代码并建立类之间的层次结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 定义一个基类
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("The animal makes a sound");
}
}

//定义一个派生类
public class Dog : Animal
{
// 可以添加新方法
public new void NewMethod()
{
Console.WriteLine("New method in DerivedClass");
}
}

//使用
public static void Main(string[] args)
{
Animal myDog = new Dog();
myDog.MakeSound(); // 输出: The animal makes a sound
myDog.NewMethod(); // 输出: New method in DerivedClass
}

⭐⭐ 虚方法

虚方法virtual method是实现多态的一种方式通过定义虚方法基类中的方法可以在派生类中被重写从而使得不同类型的对象可以以不同的方式响应同一个方法调用

  • 使用 virtual 关键字声明一个方法为虚方法
  • 虚方法可以在派生类中使用 override 关键字进行重写
  • 如果派生类没有重写该方法则默认使用基类的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("The animal makes a sound");
}
}

public class Cat : Animal
{
public override void MakeSound()
{
Console.WriteLine("The cat meows");
}
}

public static void Main(string[] args)
{
Animal myAnimal = new Animal();
Animal myCat = new Cat();

myAnimal.MakeSound(); // 输出: The animal makes a sound
myCat.MakeSound(); // 输出: The cat meows
}

⭐⭐ 隐藏方法

如果想要隐藏基类中的方法而不是重写它可以使用 new 关键字这通常用于不想改变基类行为的情况但又希望在派生类中提供一个同名的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class BaseClass
{
public virtual void MyMethod()
{
Console.WriteLine("BaseClass.MyMethod");
}

public virtual void AnotherMethod()
{
Console.WriteLine("BaseClass.AnotherMethod");
}
}

public class DerivedClass : BaseClass
{
// 隐藏 MyMethod
public new void MyMethod()
{
Console.WriteLine("DerivedClass.MyMethod");
}

// 不隐藏 AnotherMethod
public override void AnotherMethod()
{
base.AnotherMethod();
Console.WriteLine("DerivedClass.AnotherMethod");
}
}

class Program
{
static void Main(string[] args)
{
BaseClass obj = new DerivedClass();

// 调用隐藏的方法时将调用基类的方法
obj.MyMethod(); // 输出 "BaseClass.MyMethod"

// 调用重写的方法时将调用派生类的方法
obj.AnotherMethod(); // 输出 "BaseClass.AnotherMethod" 和 "DerivedClass.AnotherMethod"

// 强制转换后调用隐藏的方法时将调用派生类的方法
((DerivedClass)obj).MyMethod(); // 输出 "DerivedClass.MyMethod"
}
}

⭐⭐ 密封方法

对于方法属性或索引器sealed关键字可以防止它们被派生类重写通常与override关键字一起使用来覆盖基类中的虚成员并阻止进一步的覆盖行为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class BaseClass
{
public virtual void MyMethod()
{
Console.WriteLine("Base method");
}
}

public class DerivedClass : BaseClass
{
public sealed override void MyMethod()
{
Console.WriteLine("Derived method");
}
}

⭐⭐ 静态方法

静态方法使用 static 关键字定义它们通常用于执行与类相关的操作但不需要访问实例数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MyClass
{
// 静态方法
public static void StaticMethod()
{
Console.WriteLine("This is a static method.");
}
}

class Program
{
static void Main(string[] args)
{
MyClass.StaticMethod(); // 输出 "This is a static method."
}
}

⭐⭐ 构造函数

派生类的构造函数需要调用基类的构造函数可以通过 base 关键字来完成这一点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//示例一无参构造
public class Animal
{
public Animal()
{
Console.WriteLine("Animal created");
}
}

public class Dog : Animal
{
public Dog()
{
// 调用基类构造函数不写会默认调用
base();
Console.WriteLine("Dog created");
}
}

//示例二有参构造
public class Person
{
public string Name { get; set; }

public Person()
{
Console.WriteLine("Person default constructor called.");
}

public Person(string name)
{
Name = name;
Console.WriteLine($"Person parameterized constructor called with name: {name}");
}
}

public class Student : Person
{
public int Grade { get; set; }

public Student() : base("Unknown")
{
Console.WriteLine("Student default constructor called.");
}

public Student(string name, int grade) : base(name)
{
Grade = grade;
Console.WriteLine($"Student parameterized constructor called with name: {name} and grade: {grade}");
}
}

拆分类

partial 关键字允许你将一个类结构或方法体分成多个文件这样可以使大型类更容易管理和编写使用 partial 关键字定义的类或结构必须在所有部分中都声明为 partial但方法只需要在一个部分中声明为 partial 即可

  • partial 关键字仅适用于类结构和方法
  • partial 方法不能有方法体也不能有任何访问修饰符
  • partial 类或结构的所有部分都必须在同一命名空间内
  • 如果一个 partial 类或结构定义了构造函数析构函数属性或任何成员则这些成员必须在所有部分中都存在
  • partial 类或结构的所有部分必须在同一个项目中不能跨越项目

⭐⭐ Partial 类示例

假设我们有一个比较复杂的类 Customer它包含了基本属性和一些复杂的方法我们可以将这个类拆分为两个文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public partial class Customer
{
public string Name { get; set; }
public int Age { get; set; }

public Customer(string name, int age)
{
Name = name;
Age = age;
}
}

public partial class Customer
{
public void PrintDetails()
{
Console.WriteLine($"Name: {Name}, Age: {Age}");
}
}

⭐⭐ Partial 方法示例

partial 方法只声明了方法签名而没有实现体主要用于简化调试过程中的断点设置或者用来提供一个可以被子类重写的方法而不需要显式地声明虚方法

假设我们有一个 Logger 类我们希望在某些情况下能够扩展其行为而不改变原有类的行为在这个例子中OnLogMessage 是一个 partial 方法它允许你在不同的文件中定义它的实现这使得你可以轻松地扩展 Logger 类的功能而无需修改原始类的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public partial class Logger
{
public void LogMessage(string message)
{
// 日志记录的基本实现
Console.WriteLine(message);

// 调用 partial 方法可以在其他地方添加额外的实现
OnLogMessage(message);
}

// 这是一个 partial 方法可以在其他地方添加额外的实现
partial void OnLogMessage(string message);
}

public partial class Logger
{
// 这里是 partial 方法的实现
partial void OnLogMessage(string message)
{
// 可以在这里添加额外的日志处理逻辑
// 例如将日志写入文件
using (StreamWriter writer = new StreamWriter("log.txt", true))
{
writer.WriteLine(message);
}
}
}

抽象类

抽象类不能被实例化通常用于定义一些通用的行为这些行为可以在派生类中进一步细化可以通过使用 abstract 关键字来指定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//定义一个抽象类
public abstract class Vehicle
{
protected string Make;
protected string Model;

public Vehicle(string make, string model)
{
Make = make;
Model = model;
}

public abstract void Drive();

public virtual void DisplayInfo()
{
Console.WriteLine($"Make: {Make}, Model: {Model}");
}
}


//定义 Car类 继承 Vehicle 抽象类
public class Car : Vehicle
{
public Car(string make, string model) : base(make, model) { }

public override void Drive()
{
Console.WriteLine($"The car is driving.");
}
}

//定义 Motorcycle类 继承 Vehicle 抽象类
public class Motorcycle : Vehicle
{
public Motorcycle(string make, string model) : base(make, model) { }

public override void Drive()
{
Console.WriteLine($"The motorcycle is driving.");
}

public override void DisplayInfo()
{
base.DisplayInfo();
Console.WriteLine("Type: Motorcycle");
}
}

密封类

密封类不允许被继承可以通过使用 sealed 关键字来指定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public sealed class FinalCar
{
public string Model { get; set; }

public FinalCar(string model)
{
Model = model;
}

public void Drive()
{
Console.WriteLine($"FinalCar '{Model}' is driving.");
}
}

静态类

静态类是一种特殊的类它只能包含静态成员并且不能被实例化这意味着你不能创建一个该类的对象静态类通常用于提供工具方法或者共享资源

⭐⭐ 特点

  • 静态类中的所有成员都必须是静态的
  • 静态成员对于所有对象都是共享的因此不适合存储随对象变化而变化的数据
  • 静态成员可以在没有创建对象的情况下直接访问
  • 静态类和成员可以提高代码的效率因为它们只加载一次但过度使用可能导致难以维护的代码

⭐⭐ 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static MyStaticClass
{
//静态成员
public static int count;

//静态方法
public static void SomeMethod()
{
// ...
}

//静态构造函数
//静态类可以有一个静态构造函数它仅运行一次在第一次访问静态成员之前执行
static MyStaticClass()
{
Console.WriteLine("Static constructor called.");
}
}

泛型类

泛型类允许你在定义类时指定类型参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Pair<T>
{
public T First { get; set; }
public T Second { get; set; }

public Pair(T first, T second)
{
First = first;
Second = second;
}

public void Swap()
{
T temp = First;
First = Second;
Second = temp;
}
}

接口

接口定义了一组行为规范它只包含成员的签名而没有具体的实现接口可以被类实现也可以被多个类实现可以通过使用 interface 关键字来指定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//定义一个接口
public interface IEngine
{
void Start();
void Stop();
}

//定义一个 ElectricEngine类 实现 IEngine接口 中定义的方法
public class ElectricEngine : IEngine
{
public void Start()
{
Console.WriteLine("Electric engine started.");
}

public void Stop()
{
Console.WriteLine("Electric engine stopped.");
}
}

//定义一个 GasolineEngine类 实现 IEngine接口 中定义的方法
public class GasolineEngine : IEngine
{
public void Start()
{
Console.WriteLine("Gasoline engine started.");
}

public void Stop()
{
Console.WriteLine("Gasoline engine stopped.");
}
}

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class Program
{
static void Main()
{
// 普通类
Car myCar = new Car("Toyota", 2020);
myCar.Drive();

// 接口
IEngine electricEngine = new ElectricEngine();
electricEngine.Start();
electricEngine.Stop();

// 抽象类
Vehicle car = new Car("Honda", "Accord");
Vehicle motorcycle = new Motorcycle("Yamaha", "YZF-R1");
car.Drive();
motorcycle.Drive();
motorcycle.DisplayInfo();

// 密封类
FinalCar finalCar = new FinalCar("Ferrari");
finalCar.Drive();

// 静态类
MyStaticClass.count;
MyStaticClass.SomeMethod();

// 泛型类
Pair<string> pairOfStrings = new Pair<string>("Hello", "World");
Console.WriteLine($"Before swap: {pairOfStrings.First}, {pairOfStrings.Second}");
pairOfStrings.Swap();
Console.WriteLine($"After swap: {pairOfStrings.First}, {pairOfStrings.Second}");
}
}

反射

反射Reflection是一种强大的工具它允许程序在运行时检查和操作类型的信息这种能力可以用于获取类型的信息创建实例调用方法读写字段或属性等

Type 类

每个 .NET 对象都有一个与之关联的 Type 对象Type 类是所有类型的基础包括类接口数组类型委托类型等可以通过多种方式获取一个类型的 Type 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class MyClass
{
private string _myField;
public string MyProperty { get; set; }
public void MyMethod() { Console.WriteLine("Hello from MyMethod!"); }
}

class Program
{
static void Main(string[] args)
{
Type myType = typeof(MyClass);
//Type myType = new MyClass.GetType();
Console.WriteLine("Name: " + myType.Name); // 输出 "MyClass"
Console.WriteLine("FullName: " + myType.FullName); // 输出完整的命名空间和类名
Console.WriteLine("Namespace: " + myType.Namespace); // 输出命名空间

// 调用方法
MethodInfo method = myType.GetMethod("MyMethod");
object instance = Activator.CreateInstance(myType);
method.Invoke(instance, null);

// 设置和获取属性
PropertyInfo property = myType.GetProperty("MyProperty");
property.SetValue(instance, "New Value");
Console.WriteLine("MyProperty: " + property.GetValue(instance));

// 设置和获取字段
FieldInfo field = myType.GetField("_myField", BindingFlags.NonPublic | BindingFlags.Instance);
field.SetValue(instance, "Field Value");
Console.WriteLine("_myField: " + field.GetValue(instance));
}
}

Assembly 类

Assembly 类代表一个程序集它是 .NET 应用程序的部署单元程序集包含了一个或多个命名空间类型和资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System.Reflection;

class Program
{
static void Main(string[] args)
{
Assembly executingAssembly = Assembly.GetExecutingAssembly();
Console.WriteLine("Executing Assembly Name: " + executingAssembly.GetName().Name);

// 加载另一个程序集
Assembly anotherAssembly = Assembly.Load("mscorlib");
Console.WriteLine("Another Assembly Name: " + anotherAssembly.GetName().Name);
}
}

BindingFlags 枚举

BindingFlags 枚举用于控制反射搜索成员的方式例如你可能只想查找公共成员 (BindingFlags.Public) 或者包括私有成员 (BindingFlags.NonPublic)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Program
{
static void Main(string[] args)
{
Type myType = typeof(MyClass);

// 查找所有的公共方法
MethodInfo[] publicMethods = myType.GetMethods(BindingFlags.Public | BindingFlags.Instance);
foreach (var method in publicMethods)
{
Console.WriteLine("Public Method: " + method.Name);
}

// 查找所有的私有字段
FieldInfo[] privateFields = myType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var field in privateFields)
{
Console.WriteLine("Private Field: " + field.Name);
}
}
}

特性

特性Attributes是一种元数据机制用于向编译器运行时环境或工具提供有关程序元素如类方法属性等的附加信息特性可以用来实现诸如验证日志记录配置等功能

常用特性

特性描述
Serializable 标记类型可以被序列化
Obsolete 标记代码已过时不应再使用
AttributeUsage 控制其他自定义特性的行为
Conditional 只有在指定的预处理器符号定义时才调用该方法
DebuggerStepThrough 告诉调试器跳过该方法的调用
EditorBrowsable 控制编辑器中某些成员的可见性
IndexerName 为索引器提供替代名称
Localizable 指定字符串资源是否可本地化
NonSerialized 标记成员不参与序列化过程
STAThread 指示应用程序需要单线程 Apartment State
CLSCompliant 表明类型是否符合 CLS 规范
DebuggerDisplay 指定调试器显示对象的方式
DebuggerHidden 隐藏在调试输出中的代码
DebuggerTypeProxy 为类型提供一个代理以改变其在调试器中的显示方式
EditorDisplay 控制编辑器中如何显示类型
DebuggerBrowsable 控制成员在调试器中的可见性
CompilerGenerated 标记由编译器生成的代码
DefaultMember 指定默认成员
ComVisible 指定类型对 COM 是否可见
CallerFilePath 标记一个字符串参数并在运行时自动填充该参数为调用者的文件路径
CallerMemberName 标记一个字符串参数并在运行时自动填充该参数为调用者的成员名称
CallerLineNumber 标记一个整数参数并在运行时自动填充该参数为调用者的行号

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
[Serializable]
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}

[Obsolete("Use NewMethod instead.")]
public void OldMethod()
{
// ...
}

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class CustomAttribute : Attribute
{
public string Description { get; set; }
}

[Conditional("DEBUG")]
public static void DebugWrite(string message)
{
Console.WriteLine(message);
}

[DebuggerStepThrough]
public void Calculate(int x, int y)
{
return x * y;
}

[EditorBrowsable(EditorBrowsableState.Never)]
public void HiddenMethod()
{
// ...
}

[IndexerName("Item")]
public string this[int index] { get; set; }

[Localizable(true)]
public string Greeting { get; set; }

[NonSerialized]
private List<int> _nonSerializedList;

[STAThread]
public static void Main()
{
// ...
}

[CLSCompliant(true)]
public class CompliantClass
{
// ...
}

[DebuggerDisplay("{Name,nq} ({Age})")]
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}

[DebuggerHidden]
public static void HiddenCode()
{
// ...
}

[DebuggerTypeProxy(typeof(PersonProxy))]
public class Person
{
// ...
}

[EditorDisplay("DisplayGroup")]
public class MyClass
{
// ...
}

[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
public List<string> Items { get; set; }

[CompilerGenerated]
private void CompilerGeneratedMethod()
{
// ...
}

[DefaultMember("Name")]
public class Person
{
public string Name { get; set; }
}

[ComVisible(true)]
public class ComVisibleClass
{
// ...
}

public void Log([CallerFilePath] string filePath = "")
{
Console.WriteLine($"Called from file path: {filePath}");
}

public void Log([CallerMemberName] string memberName = "")
{
Console.WriteLine($"Called from: {memberName}");
}

public void Log([CallerLineNumber] int lineNumber = 0)
{
Console.WriteLine($"Called from line number: {lineNumber}");
}

//组合使用
public void Log([CallerMemberName] string memberName = "",
[CallerLineNumber] int lineNumber = 0,
[CallerFilePath] string filePath = "")
{
Console.WriteLine($"Called from: {filePath}:{lineNumber} in {memberName}");
}

自定义特性

通过继承 System.Attribute 类来定义一个新的特性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 自定义一个特性
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class MyAttribute : Attribute
{
public string Name { get; set; }
public int Value { get; set; }

public MyAttribute(string name, int value)
{
Name = name;
Value = value;
}
}

//使用自定义特性
[My("Example", 100)]
[My("Another", 200)]
public class MyClass
{
// ...
}

//读取特性可以使用反射来读取应用到某个元素上的特性
var attributes = (MyAttribute[])typeof(MyClass).GetCustomAttributes(typeof(MyAttribute), false);
foreach (var attr in attributes)
{
Console.WriteLine($"Name: {attr.Name}, Value: {attr.Value}");
}

异常处理

异常处理是一种用于检测和处理程序运行时错误的机制它允许你编写能够优雅地处理错误情况的健壮代码在 C# 中异常处理主要通过 try, catch, finally, 和 throw 语句来实现异常遵循一个层次结构所有异常都派生自 System.Exception 类一些常见的派生类包括 System.ArgumentExceptionSystem.NullReferenceExceptionSystem.IO.IOException 等

基本结构

1
2
3
4
5
6
7
8
9
10
11
12
try
{
// 操作文件或流的代码
}
catch (FileNotFoundException fnfe)
{
// 文件未找到时的处理逻辑
}
catch (IOException ioe)
{
// 输入输出错误时的处理逻辑
}

finally 关键字

finally 块总是执行无论是否发生异常这使得 finally 非常适合释放资源如文件句柄数据库连接等

1
2
3
4
5
6
7
8
9
10
11
12
try
{
// 可能引发异常的代码
}
catch (Exception e)
{
// 异常处理
}
finally
{
// 该代码块无论是否发生异常总是执行
}

throw 关键字

throw 语句用来抛出一个新异常或重新抛出捕获到的异常

1
2
3
4
5
6
7
8
9
10
try
{
// 可能引发异常的代码
}
catch (Exception ex)
{
//捕获到异常后打印异常的信息
Console.WriteLine(ex.Message);
throw; // 然后使用 throw 关键字重新抛出异常
}

自定义异常

除了内置的异常类之外你还可以创建自己的异常类这通常是为了更好地描述特定类型的错误或者为了提供额外的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//自定义一个异常类
public class CustomException : Exception
{
public CustomException(string message) : base(message) { }
}

//使用
try
{
// 可能引发异常的代码
}
catch (Exception ex)
{
//捕获到异常后打印异常的信息
Console.WriteLine(ex.Message);
// 然后使用 throw 关键字重新抛出异常
// 抛出自定义异常
throw new CustomException("This is a custom exception.");;
}

异常过滤器

从 C# 7.0 开始你可以使用异常过滤器来决定是否要捕获一个异常

1
2
3
4
5
6
7
8
try
{
// 可能引发异常的代码
}
catch (Exception e) when (e is ArgumentException && e.Message.Contains("length"))
{
// 只捕获特定的 ArgumentException
}

数据存储

栈 (Stack)

栈是一种后进先出 (LIFO, Last In First Out) 的数据结构在 C# 中用于存储局部变量和方法调用时的参数栈上的内存分配和释放速度非常快因为它是通过指针的移动来实现的例如压栈和弹栈操作

⭐⭐ 特点

  • 自动管理由编译器自动分配和释放
  • 速度快分配和释放内存的速度很快
  • 固定大小分配给栈的空间通常是固定的
  • 安全性栈上分配的变量不能被其他线程访问
  • 局部作用域栈中的变量只在其定义的作用域内可见

⭐⭐ 示例

1
2
3
4
void Method()
{
int x = 10; // x 存储在栈中
}

堆 (Heap)

堆是用于动态内存分配的区域主要用来存储对象实例和数组当一个对象或数组创建时其实际的数据会存储在堆上并通过引用类型如类接口等指向这些数据

⭐⭐ 特点

  • 手动管理程序员需要显式地分配和释放内存通过 new 关键字分配通过垃圾回收器自动回收
  • 可变大小可以动态调整大小
  • 速度较慢相对于栈来说分配和释放内存的速度较慢
  • 共享性堆上的对象可以被多个线程共享
  • 全局作用域堆上的对象可以在整个程序的生命周期内存在

⭐⭐ 示例

1
2
3
4
5
6
7
8
9
10
class MyClass
{
public int Value; // 变量存储在栈中
}

void Method()
{
MyClass obj = new MyClass(); // 对象存储在堆上obj 是指向该对象的引用
obj.Value = 10;
}

静态存储区

静态存储区通常指的是程序运行期间一直存在的内存区域用于存储静态变量常量等

⭐⭐ 特点

  • 生存期静态变量在整个应用程序的生命周期内存在
  • 初始化静态变量可以在类的静态构造函数中初始化或者在声明时直接初始化
  • 访问可以通过类名直接访问静态变量而不需要创建类的实例

⭐⭐ 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MyClass
{
public static int StaticValue = 10; // 静态变量

public MyClass()
{
Console.WriteLine(StaticValue); // 访问静态变量
}
}

class Program
{
static void Main(string[] args)
{
MyClass.StaticValue = 20; // 修改静态变量
MyClass myInstance = new MyClass(); // 输出 20
}
}

垃圾回收

垃圾回收 (Garbage Collection, GC) 是一种自动内存管理机制它负责检测和释放不再使用的对象占用的内存这一机制极大地简化了开发者的任务因为他们不需要手动管理内存的分配和释放

⭐⭐ 工作原理

垃圾回收的工作原理基于可达性分析 (Reachability Analysis)它通过追踪对象之间的引用关系确定哪些对象是可达的被活动对象引用而哪些对象是不可达的不再被引用不可达的对象被认为是垃圾将被垃圾回收器回收

  • 可达性分析
    • 根对象GC 从一组根对象开始分析这些根对象通常是当前活动的栈帧中的局部变量静态字段中的对象以及全局处理器表中的对象
    • 对象图遍历GC 会遍历从根对象开始的对象图找出所有可达的对象
    • 不可达对象那些无法通过根对象到达的对象被视为垃圾将被回收

⭐⭐ 垃圾回收的过程

  • 标记与清除 (Mark and Sweep)
    • 标记 (Mark)GC 会标记所有从根对象可达的对象
    • 清除 (Sweep)未被标记的对象将被回收而标记的对象则保持不变
  • 压缩 (Compaction)
    • 在清除阶段之后存活的对象可能会被移动到内存的一端以减少内存碎片
    • 这个过程称为压缩它可以使得内存变得更加紧凑从而提高内存分配效率
  • 分代算法 (Generational Algorithm)
    • GC 将堆划分为几代新创建的对象最初位于第一代
    • 第一代的对象被回收后存活的对象会被提升到第二代
    • 这个过程继续进行直到对象达到最终代
    • 较年轻的代回收频率较高因为新对象往往寿命较短较老的代回收频率较低

⭐⭐ 垃圾回收的触发

  • 自动触发
    • 当堆内存不足时GC 会自动触发
    • GC 也可能根据应用的需求和堆的使用情况定期运行
  • 手动触发
    • 可以通过调用 GC.Collect() 方法来强制进行垃圾回收
    • 但是手动触发 GC 通常不推荐因为它可能导致性能问题

⭐⭐ 非托管资源的处理

对于非托管资源如文件句柄数据库连接等需要实现 IDisposable 接口并调用 Dispose 方法来释放资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//假设我们有一个简单的类它持有非托管资源如文件句柄可以使用 using 语句会自动调用对象的 Dispose 方法来进行清理

// 使用示例一
using (FileStream fs = new FileStream("path", FileMode.Open))
{
// 使用 FileStream 对象
}

// 使用示例二
public class ResourceHolder : IDisposable
{
private FileStream _fileStream;

public ResourceHolder()
{
_fileStream = new FileStream("example.txt", FileMode.Open);
}

public void Dispose()
{
_fileStream.Dispose();
}
}

using (var holder = new ResourceHolder())
{
// 使用 ResourceHolder
}

索引器

索引器Indexer是一种特殊的成员它允许类或结构的实例使用索引来访问其成员就像数组一样实际上索引器提供了一种通过索引值来获取或设置对象内部数据的方法

  • 索引器可以重载你可以定义多个索引器每个索引器有不同的参数类型
  • 索引器可以包含参数除了索引外还可以包含其他参数这些参数可以用于更复杂的索引操作
  • 索引器可以是只读或只写如果只需要读取功能可以省略 set 访问器如果只需要设置功能则可以省略 get 访问器

简单索引器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//定义一个索引器
public class MyList
{
private int[] items;

public MyList(int size)
{
items = new int[size];
}

// 索引器定义
public int this[int index]
{
get
{
if (index < 0 || index >= items.Length)
throw new IndexOutOfRangeException("Invalid index.");
return items[index];
}
set
{
if (index < 0 || index >= items.Length)
throw new IndexOutOfRangeException("Invalid index.");
items[index] = value;
}
}
}

//使用
class Program
{
static void Main(string[] args)
{
MyList myList = new MyList(5);
myList[0] = 10; // 设置第一个元素的值为10
int value = myList[0]; // 获取第一个元素的值
}
}

复杂索引器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class MyMatrix
{
private int[,] matrix;

public MyMatrix(int rows, int cols)
{
matrix = new int[rows, cols];
}

public int this[int row, int col]
{
get
{
return matrix[row, col];
}
set
{
matrix[row, col] = value;
}
}
}

//使用
class Program
{
static void Main(string[] args)
{
MyMatrix myMatrix = new MyMatrix(5, 8);
myMatrix[0][0] = 10; // 设置第一个元素的值为10
int value = myMatrix[0][0]; // 获取第一个元素的值
}
}

运算符重载

运算符重载是一种多态性形式它允许您为已定义的类型如自定义类或结构提供现有运算符的新实现这使得您能够以类似于内置类型的自然方式使用这些类型

基本规则

  • 不能创建新的运算符只能重载现有的运算符
  • 不能改变运算符的优先级和结合性
  • 重载的运算符必须至少有一个操作数是用户定义的类型
  • 重载运算符的返回类型不能更改
  • 重载运算符不改变操作数的数量
  • 重载运算符不能声明为静态但可以使用实例方法来实现

常见运算符的重载

  • 一元运算符如 +-!~++
  • 二元运算符如 +-*/%<><=>===!=

重载加法运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//定义一个表示二维向量的类 Vector2D我们可以重载加法运算符来添加两个向量
public struct Vector2D
{
public double X;
public double Y;

public Vector2D(double x, double y)
{
X = x;
Y = y;
}

// 重载 + 运算符
public static Vector2D operator +(Vector2D v1, Vector2D v2)
{
return new Vector2D(v1.X + v2.X, v1.Y + v2.Y);
}
}

// 使用示例
class Program
{
static void Main(string[] args)
{
Vector2D v1 = new Vector2D(1, 2);
Vector2D v2 = new Vector2D(3, 4);
Vector2D result = v1 + v2;
}
}

重载比较运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public struct Point
{
public int X;
public int Y;

public Point(int x, int y)
{
X = x;
Y = y;
}

// 重载 == 运算符
public static bool operator ==(Point p1, Point p2)
{
return p1.X == p2.X && p1.Y == p2.Y;
}

// 重载 != 运算符
public static bool operator !=(Point p1, Point p2)
{
return !(p1 == p2);
}

// 重载 < 运算符
public static bool operator <(Point p1, Point p2)
{
if (p1.X == p2.X)
return p1.Y < p2.Y;
return p1.X < p2.X;
}

// 重载 > 运算符
public static bool operator >(Point p1, Point p2)
{
return p2 < p1;
}
}

集合

集合Collections是.NET Framework的一部分主要用于存储检索和操作一组数据C# 提供了多种类型的集合类来满足不同的需求这些集合类主要位于 System.Collections 和 System.Collections.Generic 命名空间中

非泛型集合

⭐⭐ ArrayList可变大小的数组可以存储任何类型的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ArrayList list = new ArrayList();

//添加元素
list.Add("apple");
list.Add(123);

//访问元素
string firstItem = (string)list[0];
int number = (int)list[1];

//删除元素
list.Remove("banana");
list.RemoveAt(0);

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

⭐⭐ Hashtable键值对存储键唯一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Hashtable table = new Hashtable();

//添加元素如果键已经存在则会抛出异常
table.Add("key1", "value1");
table.Add(123, "another value");

//访问元素
string value1 = (string)table["key1"];
string value2 = (string)table[123];

//删除元素
table.Remove("key1");

//遍历
foreach (DictionaryEntry entry in table) {
Console.WriteLine("Key: {0}, Value: {1}", entry.Key, entry.Value);
}

⭐⭐ Queue先进先出FIFO的数据结构队列是一种数据结构其中新元素添加到队列的尾部而旧元素从队列的头部移除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Queue queue = new Queue();

//添加元素
queue.Enqueue("first");
queue.Enqueue("second");

//访问元素/取到后不删除/如果队列为空调用 Peek 将抛出 InvalidOperationException 异常
string firstFruit = queue.Peek();

//删除元素/取到后删除/如果队列为空调用 Dequeue 将抛出 InvalidOperationException 异常
string fruit = queue.Dequeue();

Console.WriteLine(queue.Dequeue()); // 输出: first

//遍历
foreach (var item in queue) {
Console.WriteLine(item);
}

⭐⭐ Stack后进先出LIFO的数据结构栈是一种数据结构其中新元素添加到栈的顶部而旧元素也从栈的顶部移除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Stack stack = new Stack();

//添加元素
stack.Push("apple");
stack.Push("banana");

//访问栈顶元素但不会删除它/如果栈为空调用 Peek 会抛出 InvalidOperationException 异常
string topFruit = stack.Peek();
Console.WriteLine(topFruit); // 输出: banana

//删除元素/取到后删除/如果栈为空调用 Pop 将抛出 InvalidOperationException 异常
string fruit = stack.Pop();
Console.WriteLine(fruit); // 输出: banana

//遍历/由于栈的特性遍历时的顺序是从栈底到栈顶
foreach (var item in stack) {
Console.WriteLine(item);
}

⭐⭐ BitArray位数组用于存储二进制位它可以用来表示固定长度的位数组通常用于处理位图压缩算法位字段等场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//初始化一指定长度
BitArray bits = new BitArray(10);

//初始化二从布尔数组
bool[] initialBits = { true, false, true };
BitArray bits = new BitArray(initialBits);

//初始化三从整数
int initialValue = 5; // 二进制表示为 000...00101
BitArray bits = new BitArray(new int[] { initialValue });

//初始化四从字节数组
byte[] initialBytes = { 0x01, 0x02 }; // 二进制表示为 00000001 00000010
BitArray bits = new BitArray(initialBytes);

//设置位使用 Set 方法将指定索引处的位设置为 true
bits.Set(2, true);

//清除位使用 Set 方法将指定索引处的位设置为 false
bits.Set(2, false);

//获取位使用索引运算符 [] 或 Get 方法获取指定索引处的位值
bool bitValue = bits[2];
bool bitValue2 = bits.Get(2);

//与 (AND)使用 And 方法执行两个 BitArray 的按位与操作
BitArray result = (BitArray)bits.Clone();
result.And(otherBits);

//或 (OR)使用 Or 方法执行两个 BitArray 的按位或操作
BitArray result = (BitArray)bits.Clone();
result.Or(otherBits);

//异或 (XOR)使用 Xor 方法执行两个 BitArray 的按位异或操作
BitArray result = (BitArray)bits.Clone();
result.Xor(otherBits);

//取反 (NOT)使用 Not 方法对 BitArray 进行按位取反操作
BitArray result = (BitArray)bits.Clone();
result.Not();

//遍历
foreach (bool bit in bits) {
Console.WriteLine(bit);
}

泛型集合

⭐⭐ List类似于 ArrayList但类型安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
List<string> names = new List<string>();

/********************************* 添加元素 *************************************/

//向列表末尾添加一个元素
names.Add("Alice");
names.Add("Bob");

//向列表末尾添加一系列元素
names.AddRange(new string[] { "Charlie", "Diana" });

//在指定位置插入一个元素
list.Insert(0, "first item");

//在指定位置插入一系列元素
list.InsertRange(2, new string[] { "middle item" });

/********************************* 移除元素 *************************************/

//移除列表中第一次出现的指定元素
names.Remove("Bob");

//移除列表中指定索引处的元素
names.RemoveAt(0);

//移除指定范围内的元素
list.RemoveRange(1, 2); // 移除索引1和2处的元素

//移除所有满足指定条件的元素
list.RemoveAll(item => item.StartsWith("A")); // 移除所有以"A"开头的元素

//移除列表中的所有元素
list.Clear();

/********************************* 获取元素 *************************************/

//访问元素
string firstPerson = names[0];

//获取列表中指定范围内的元素
List<string> range = list.GetRange(1, 2); // 获取索引1和2处的元素

/********************************* 查找元素 *************************************/

//判断列表是否包含指定元素
bool containsItem = list.Contains("item");

//返回指定元素在列表中的索引如果没有找到则返回 -1
int index = list.IndexOf("item");

//查找满足指定条件的第一个元素
string foundItem = list.Find(item => item.Length > 5);

//查找满足指定条件的所有元素
List<string> foundItems = list.FindAll(item => item.Length > 5);

//查找满足指定条件的第一个元素的索引
int foundIndex = list.FindIndex(item => item.StartsWith("A"));

//查找满足指定条件的最后一个元素
string foundLastItem = list.FindLast(item => item.EndsWith("e"));

//查找满足指定条件的最后一个元素的索引
int foundLastIndex = list.FindLastIndex(item => item.EndsWith("e"));

//测试列表中的所有元素是否满足指定条件
bool allAreLongerThanFive = list.TrueForAll(item => item.Length > 5);

/********************************* 其他操作 *************************************/

//对列表中的元素进行排序
list.Sort(); // 默认排序
list.Sort(new MyComparer()); // 自定义比较器

//在已排序的列表中搜索指定元素
int position = list.BinarySearch("item");

//反转列表中元素的顺序
list.Reverse();

//将列表转换为数组
string[] array = list.ToArray();

//将列表中的元素复制到数组中
string[] targetArray = new string[list.Count];
list.CopyTo(targetArray, 0);

//获取列表中的元素数量
int count = list.Count;

//获取列表的容量
int capacity = list.Capacity;

//减少列表的容量到实际所需的最小值
list.TrimExcess();

//遍历
foreach (var name in names) {
Console.WriteLine(name);
}

⭐⭐ HashSet基于哈希表的集合元素不重复且无序

1
2
3
4
HashSet<int> numbers = new HashSet<int>();
numbers.Add(1);
numbers.Add(2);
numbers.Add(1); // 不会被添加

⭐⭐ LinkedList双向链表适合频繁插入和删除的场景

1
2
3
LinkedList<string> words = new LinkedList<string>();
words.AddLast("hello");
words.AddLast("world");

⭐⭐ Queue先进先出FIFO类型安全

1
2
3
4
Queue<string> queue = new Queue<string>();
queue.Enqueue("first");
queue.Enqueue("second");
Console.WriteLine(queue.Dequeue()); // 输出: first

⭐⭐Stack后进先出LIFO类型安全

1
2
3
4
Stack<string> stack = new Stack<string>();
stack.Push("first");
stack.Push("second");
Console.WriteLine(stack.Pop()); // 输出: second

⭐⭐ Dictionary<TKey, TValue>键值对存储键唯一类型安全

1
2
3
Dictionary<string, int> scores = new Dictionary<string, int>();
scores["Alice"] = 95;
scores["Bob"] = 87;

其他集合

⭐⭐ SortedSet保持排序顺序的集合元素不重复

1
2
3
4
5
6
7
8
SortedSet<int> set = new SortedSet<int>();
set.Add(3);
set.Add(1);
set.Add(2);
foreach (var item in set)
{
Console.WriteLine(item); // 输出: 1, 2, 3
}

⭐⭐ BlockingCollection线程安全的队列支持生产者/消费者模式可以在等待数据时阻塞

1
2
3
4
BlockingCollection<int> bc = new BlockingCollection<int>();
bc.Add(1);
bc.Add(2);
int number = bc.Take(); // 阻塞等待数据

⭐⭐ ConcurrentBag线程安全的集合支持并发添加和移除元素

1
2
3
4
ConcurrentBag<int> bag = new ConcurrentBag<int>();
bag.Add(1);
bag.Add(2);
int number = bag.TryTake(out number); // 尝试移除一个元素

⭐⭐ ConcurrentQueue线程安全的队列支持并发读写操作

1
2
3
4
5
ConcurrentQueue<int> cq = new ConcurrentQueue<int>();
cq.Enqueue(1);
cq.Enqueue(2);
int number;
cq.TryDequeue(out number); // 尝试移除一个元素

⭐⭐ ConcurrentStack线程安全的栈支持并发读写操作

1
2
3
4
5
ConcurrentStack<int> cs = new ConcurrentStack<int>();
cs.Push(1);
cs.Push(2);
int number;
cs.TryPop(out number); // 尝试移除一个元素

⭐⭐ SortedDictionary<TKey, TValue>保持键排序顺序的字典

1
2
3
4
5
6
7
SortedDictionary<string, int> dict = new SortedDictionary<string, int>();
dict.Add("b", 2);
dict.Add("a", 1);
foreach (var item in dict)
{
Console.WriteLine(item.Key + ": " + item.Value); // 输出: a: 1, b: 2
}

⭐⭐ConcurrentDictionary<TKey, TValue>线程安全的字典支持并发读写操作

1
2
3
4
ConcurrentDictionary<string, int> cd = new ConcurrentDictionary<string, int>();
cd.TryAdd("key", 1);
int value;
cd.TryGetValue("key", out value);

⭐⭐KeyValuePair<TKey, TValue>表示键值对的结构体常用作字典的枚举项

1
KeyValuePair<string, int> pair = new KeyValuePair<string, int>("key", 1);

⭐⭐KeyValuePair<TKey, TValue>[]键值对数组可以作为简单字典的替代品

1
2
3
4
5
KeyValuePair<string, int>[] pairs = new KeyValuePair<string, int>[]
{
new KeyValuePair<string, int>("key1", 1),
new KeyValuePair<string, int>("key2", 2)
};

只读集合

希望将一个集合暴露给其他代码使用但又不想让这些代码能够修改集合的内容时可以使用 AsReadOnly 方法将集合转换为只读形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Program
{
static void Main()
{
// 创建一个 List 集合
List<string> originalList = new List<string>() { "apple", "banana", "cherry" };

// 使用 AsReadOnly 方法创建一个只读视图
IList<string> readOnlyList = originalList.AsReadOnly();

// 尝试添加一个元素到只读列表
try
{
readOnlyList.Add("date"); // 这里会抛出异常
}
catch (NotSupportedException ex)
{
Console.WriteLine("Exception caught: " + ex.Message);
}

// 正常地读取只读列表中的元素
foreach (string item in readOnlyList)
{
Console.WriteLine(item);
}
}
}

多线程

基本概念

  • 线程是程序执行流的最小单位一个标准的单线程程序一次只能做一件事
  • 多线程允许程序同时执行多个任务或操作
  • 并发是指程序能够同时处理多个任务的能力但这些任务不一定在同一时刻运行

创建线程

⭐⭐ 示例一使用 Thread 类

1
2
3
4
5
6
7
8
9
10
11
12
//线程要执行的函数
void ThreadFunction()
{
// 线程要执行的代码
}

//创建 Thread 对象
ThreadStart threadStart = new ThreadStart(ThreadFunction)
Thread thread = new Thread(threadStart);

//启动线程
thread.Start();

⭐⭐ 示例二使用 Task 类

1
2
3
4
5
6
7
8
//线程要执行的函数
void TaskFunction()
{
// 线程要执行的代码
}

// 创建 Task 对象
Task task = Task.Run(() => TaskFunction());

线程休眠

1
Thread.Sleep(1000); // 线程休眠 1 秒单位毫秒

等待线程

1
2
3
4
5
// 等待 thread 完成
thread.Join();

// 等待 task 完成
task.Wait();

取消线程

⭐⭐ 示例一

1
thread.Abort();

⭐⭐ 示例二

使用 CancellationToken 可以优雅地取消任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;

Task.Run(async () =>
{
try
{
await DoWorkAsync(token);
}
catch (OperationCanceledException)
{
Console.WriteLine("Task was canceled.");
}
}, token);

// 取消任务
cts.Cancel();

同步锁

使用 lock 关键字来确保同一时间只有一个线程可以访问临界区

1
2
3
4
5
6
7
8
9
private object _lockObject = new object();

void AccessData()
{
lock (_lockObject)
{
// 访问共享资源
}
}

并行循环

1
2
int[] numbers = { 1, 2, 3, 4, 5 };
Parallel.ForEach(numbers, number => Console.WriteLine(number));

并行 LINQ

1
var result = numbers.AsParallel().Where(n => n > 2).ToArray();

简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
static void Main(string[] args)
{
Console.WriteLine("Main thread started.");

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

// 创建并启动任务
Task task = Task.Run(() => TaskFunction());

// 等待线程完成
thread.Join();

// 等待任务完成
task.Wait();

Console.WriteLine("Main thread finished.");
}

static void ThreadFunction()
{
Console.WriteLine("Thread function running.");
Thread.Sleep(2000);
Console.WriteLine("Thread function finished.");
}

static void TaskFunction()
{
Console.WriteLine("Task function running.");
Task.Delay(2000).Wait();
Console.WriteLine("Task function finished.");
}
}

线程池

线程池Thread Pool是.NET运行时提供的一种管理线程的机制它能够有效地管理和复用线程从而提高应用程序的性能线程池的主要优点包括减少创建和销毁线程的成本避免过多线程导致的系统资源耗尽以及更好地控制并发执行的任务数量

⭐⭐执行委托任务

通过调用ThreadPool.QueueUserWorkItem方法来提交一个任务给线程池这个方法接受一个WaitCallback类型的委托作为参数该委托是一个异步回调函数它接受一个object类型的参数并返回void

1
2
3
4
5
6
7
8
9
10
public void UseThreadPool()
{
ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), "Hello, ThreadPool!");
}

private static void DoWork(object state)
{
string message = (string)state;
Console.WriteLine(message);
}

⭐⭐注册等待句柄

ThreadPool.RegisterWaitForSingleObject可以让你注册一个等待句柄当特定的对象变为信号状态时就会激活一个回调函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void RegisterWaitHandle()
{
AutoResetEvent autoResetEvent = new AutoResetEvent(false);

ThreadPool.RegisterWaitForSingleObject(
autoResetEvent,
new WaitOrTimerCallback(WaitCallbackMethod),
null,
Timeout.Infinite,
true);

// Signal the event after some time.
Thread.Sleep(3000);
autoResetEvent.Set();
}

private static void WaitCallbackMethod(object state, bool timedOut)
{
if (!timedOut)
{
Console.WriteLine("The object has been signaled.");
}
}

⭐⭐定时器

通过ThreadPool.RegisterTimer方法ThreadPool还可以用来创建定时器

1
2
3
4
5
6
7
8
9
public void CreateTimer()
{
var timer = new Timer(TimerCallbackMethod, null, 0, 1000); // 第一次立即触发之后每秒触发一次
}

private static void TimerCallbackMethod(object state)
{
Console.WriteLine("Timer callback method called at: " + DateTime.Now);
}

⭐⭐设置线程池参数

1
2
3
4
5
6
7
8
9
10
public void SetThreadPoolParameters()
{
int workerThreads, completionPortThreads;
ThreadPool.GetMinThreads(out workerThreads, out completionPortThreads);
Console.WriteLine("Minimum Worker Threads: {0}", workerThreads);

ThreadPool.SetMinThreads(25, 25);
ThreadPool.GetMinThreads(out workerThreads, out completionPortThreads);
Console.WriteLine("New Minimum Worker Threads: {0}", workerThreads);
}

文件处理

Directory 类

Directory 类提供了静态方法来执行与目录相关的基本操作如创建删除移动目录以及列举目录中的文件和子目录等

创建目录

1
2
//创建指定路径的新目录
Directory.CreateDirectory(@"C:\example\newFolder");

列出目录项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//列出指定路径下的所有子目录
foreach (var directory in Directory.EnumerateDirectories(@"C:\example"))
{
Console.WriteLine(directory);
}

//根据模式列出指定路径下的子目录
foreach (var directory in Directory.EnumerateDirectories(@"C:\example", "temp*"))
{
Console.WriteLine(directory);
}

//列出指定路径下的所有文件
foreach (var file in Directory.EnumerateFiles(@"C:\example"))
{
Console.WriteLine(file);
}

//根据模式列出指定路径下的文件
foreach (var file in Directory.EnumerateFiles(@"C:\example", "*.txt"))
{
Console.WriteLine(file);
}

移动目录

1
2
//将目录从一个位置移动到另一个位置可以同时重命名目录
Directory.Move(@"C:\example\oldFolder", @"C:\example\newFolder");

删除目录

1
2
3
4
5
//删除指定路径的目录如果目录不是空的则会抛出异常
Directory.Delete(@"C:\example\emptyFolder");

//删除指定路径的目录如果 recursive 设置为 true则会删除目录及其所有子目录和文件
Directory.Delete(@"C:\example\nonEmptyFolder", true);

检查目录是否存在

1
2
3
4
5
6
7
8
9
//确定指定路径的目录是否存在
if (Directory.Exists(@"C:\example"))
{
Console.WriteLine("Directory exists.");
}
else
{
Console.WriteLine("Directory does not exist.");
}

获取当前工作目录

1
2
string currentDirectory = Directory.GetCurrentDirectory();
Console.WriteLine(currentDirectory);

设置当前工作目录

1
Directory.SetCurrentDirectory(@"C:\example");

其他方法

  • GetCreationTime(string path): 获取指定路径下目录的创建时间
  • GetLastAccessTime(string path): 获取指定路径下目录的最后访问时间
  • GetLastWriteTime(string path): 获取指定路径下目录的最后写入时间

DirectoryInfo 类

DirectoryInfo 类提供了面向对象的方式来表示目录并允许你执行各种目录操作相比于 Directory 类提供的静态方法DirectoryInfo 类提供了一个更丰富的 API允许你更方便地管理目录的信息和行为

创建实例

1
2
3
4
5
//通过构造函数
DirectoryInfo directoryInfo = new DirectoryInfo(@"C:\example");

//通过 Directory 类的静态方法返回的 DirectoryInfo 对象
DirectoryInfo directoryInfo = Directory.CreateDirectory(@"C:\example");

属性

Exists: 获取一个值

1
2
3
4
5
6
7
8
9
//当前 DirectoryInfo 对象所引用的目录是否存在
if (directoryInfo.Exists)
{
Console.WriteLine("Directory exists.");
}
else
{
Console.WriteLine("Directory does not exist.");
}

FullName: 获取当前目录的完整路径

1
Console.WriteLine(directoryInfo.FullName);

Name: 获取当前目录的名称

1
Console.WriteLine(directoryInfo.Name);

Parent: 获取当前目录的父目录

1
2
DirectoryInfo parentDir = directoryInfo.Parent;
Console.WriteLine(parentDir.FullName);

Root: 获取当前目录所在的根目录

1
2
DirectoryInfo rootDir = directoryInfo.Root;
Console.WriteLine(rootDir.FullName);

CreationTime: 获取目录的创建时间

1
Console.WriteLine(directoryInfo.CreationTime);

LastAccessTime: 获取目录的最后访问时间

1
Console.WriteLine(directoryInfo.LastAccessTime);

LastWriteTime: 获取目录的最后写入时间

1
Console.WriteLine(directoryInfo.LastWriteTime);

方法

创建一个子目录

1
2
3
//在当前目录中创建一个子目录并返回一个新的 DirectoryInfo 对象
DirectoryInfo subDir = directoryInfo.CreateSubdirectory("subfolder");
Console.WriteLine(subDir.FullName);

删除当前目录

1
2
//删除当前目录如果 recursive 设置为 true则删除该目录及其所有子目录和文件
directoryInfo.Delete(true); // 删除目录及其所有子目录和文件

将当前目录移动到新的位置

1
directoryInfo.MoveTo(@"C:\newlocation");

枚举当前目录中的所有子目录

1
2
3
4
foreach (DirectoryInfo dir in directoryInfo.EnumerateDirectories())
{
Console.WriteLine(dir.FullName);
}

枚举当前目录中符合特定搜索模式的所有子目录

1
2
3
4
foreach (DirectoryInfo dir in directoryInfo.EnumerateDirectories("temp*"))
{
Console.WriteLine(dir.FullName);
}

枚举当前目录中的所有文件

1
2
3
4
foreach (FileInfo file in directoryInfo.EnumerateFiles())
{
Console.WriteLine(file.FullName);
}

枚举当前目录中符合特定搜索模式的所有文件

1
2
3
4
foreach (FileInfo file in directoryInfo.EnumerateFiles("*.txt"))
{
Console.WriteLine(file.FullName);
}

StreamReader 类

StreamReader 类是 C# 中用于读取文本文件的类它位于 System.IO 命名空间中并且可以用来以不同的方式读取文件内容如逐字符逐行或一次性读取整个文件

构造函数

  • StreamReader(string path): 使用指定的文件路径打开文件进行读取
  • StreamReader(string path, Encoding encoding): 使用指定的编码和文件路径打开文件进行读取
  • StreamReader(string path, bool leaveOpen): 使用指定的文件路径打开文件并且可以根据 leaveOpen 参数决定是否在关闭 StreamReader 对象后保持基础流的打开状态
  • StreamReader(Stream stream): 使用指定的字节流进行读取
  • StreamReader(Stream stream, Encoding encoding): 使用指定的编码和字节流进行读取
  • StreamReader(Stream stream, bool leaveOpen): 使用指定的字节流并且可以根据 leaveOpen 参数决定是否在关闭 StreamReader 对象后保持基础流的打开状态

方法

  • Read(): 从当前流中读取一个字符
  • ReadLine(): 从当前流中读取一行字符
  • ReadToEnd(): 从当前流中读取直到结束的所有字符并返回一个字符串
  • Peek(): 获取当前流中的下一个字符但不移除该字符
  • Close(): 关闭当前流并释放与 StreamReader 关联的所有资源

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using System;
using System.IO;

class Program {
static void Main() {
string filePath = "example.txt";
using (StreamReader reader = new StreamReader(filePath)) {
string line;
while ((line = reader.ReadLine()) != null) {
Console.WriteLine(line);
}
}
}
}

StreamWriter 类

StreamWriter 类是 C# 中用于写入文本到文件的类它同样位于 System.IO 命名空间中提供了一系列的方法来方便地向文件中写入文本数据

构造函数

  • StreamWriter(string path): 使用指定的文件路径创建一个新的 StreamWriter 实例如果文件存在则将其内容清空如果文件不存在则创建一个新文件
  • StreamWriter(string path, bool append): 如果 append 参数为 true则在文件末尾追加如果为 false则创建新文件或清空现有文件
  • StreamWriter(string path, bool append, Encoding encoding): 指定编码其他行为同上
  • StreamWriter(Stream stream): 使用指定的字节流创建 StreamWriter 实例
  • StreamWriter(Stream stream, Encoding encoding): 使用指定的编码和字节流创建 StreamWriter 实例
  • StreamWriter(Stream stream, Encoding encoding, int bufferSize): 使用指定的编码字节流以及缓冲区大小创建 StreamWriter 实例

方法

  • Write(string text): 向流中写入指定的字符串
  • WriteLine(string text): 向流中写入指定的字符串然后写入当前系统的换行符
  • Flush(): 刷新此流强制任何已缓冲但尚未写入的基础流的数据写入
  • Close(): 关闭当前流并释放与之关联的所有资源
  • Dispose(): 释放与当前 StreamWriter 实例关联的所有资源

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
using System;
using System.IO;

class Program {
static void Main() {
string filePath = "example.txt";
// 创建 StreamWriter 对象并设置为追加模式
using (StreamWriter writer = new StreamWriter(filePath, true)) {
writer.WriteLine("Hello, World!");
writer.WriteLine("This is an example of writing to a file.");
}
}
}

FileStream 类

FileStream 类是 C# 中用于处理文件流的重要类之一它位于 System.IO 命名空间下这个类主要用于处理文件的二进制读写操作可以用于读取图片音频视频等二进制文件也可以用于更高效地读写文本文件

构造函数

  • FileStream(string path, FileMode mode): 使用指定的路径和文件模式打开文件
  • FileStream(string path, FileMode mode, FileAccess access): 使用指定的路径文件模式和访问类型打开文件
  • FileStream(string path, FileMode mode, FileAccess access, FileShare share): 使用指定的路径文件模式访问类型和共享模式打开文件
  • FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize): 使用指定的路径文件模式访问类型共享模式和缓冲区大小打开文件
  • FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync): 使用指定的路径文件模式访问类型共享模式缓冲区大小和异步标志打开文件

FileMode

FileMode文件模式枚举定义了如何打开或创建文件

  • Create: 创建新文件如果文件已存在则会被删除并重新创建
  • CreateNew: 创建新文件如果文件已存在则引发异常
  • Open: 打开现有文件如果文件不存在则引发异常
  • OpenOrCreate: 打开现有文件或创建新文件
  • Truncate: 打开现有文件并截断其长度至零如果文件不存在则创建新文件
  • Append: 打开现有文件并将指针置于文件末尾以进行追加操作如果文件不存在则创建新文件

FileAccess

FileAccess文件访问类型枚举定义了对文件的访问类型

  • Read: 只读访问
  • Write: 只写访问
  • ReadWrite: 读写访问

FileShare

FileShare文件共享模式枚举定义了文件的共享模式

  • None: 不共享
  • Read: 允许其他进程读取文件
  • Write: 允许其他进程写入文件
  • Delete: 允许其他进程删除文件
  • Inherit: 文件句柄可由子进程继承
  • 组合模式: 如 Read | Write 表示读写共享

方法

  • Read(byte[] buffer, int offset, int count): 从当前流中读取指定数量的字节并将它们存储在给定的缓冲区中
  • Write(byte[] buffer, int offset, int count): 将指定数量的字节从给定的缓冲区写入当前流
  • Flush(): 将任何未写入的缓冲数据刷新到磁盘
  • SetLength(long value): 设置当前流的长度
  • Close(): 关闭当前流并释放所有相关资源
  • Dispose(): 释放与当前对象相关的所有资源

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using System;
using System.IO;

class Program {
static void Main() {
string filePath = "example.bin";

// 写入文件
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) {
byte[] data = { 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64 }; // "Hello World"
fs.Write(data, 0, data.Length);
}

// 读取文件
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) {
byte[] buffer = new byte[1024];
int bytesRead = fs.Read(buffer, 0, buffer.Length);
Console.WriteLine(BitConverter.ToString(buffer, 0, bytesRead));
}
}
}

BinaryReader 类

BinaryReader 类是 C# 中用于从流中读取基本数据类型的类它位于 System.IO 命名空间中BinaryReader 类通过一个 Stream 对象来读取数据可以方便地读取各种基本数据类型如整数浮点数字符串等

构造函数

  • BinaryReader(Stream input): 使用指定的流对象创建 BinaryReader
  • BinaryReader(Stream input, Encoding encoding): 使用指定的流对象和编码创建 BinaryReader
  • BinaryReader(Stream input, Encoding encoding, bool leaveOpen): 使用指定的流对象编码和一个布尔值来控制是否在 BinaryReader 关闭时保留基础流的打开状态

属性

  • BaseStream: 获取当前 BinaryReader 使用的 Stream
  • Encoding: 获取当前 BinaryReader 使用的 Encoding

方法

  • ReadByte(): 从当前流中读取单个字节
  • ReadSByte(): 从当前流中读取单个带符号的字节
  • ReadBoolean(): 从当前流中读取一个布尔值
  • ReadChar(): 从当前流中读取一个字符
  • ReadInt16(): 从当前流中读取一个 16 位整数
  • ReadInt32(): 从当前流中读取一个 32 位整数
  • ReadInt64(): 从当前流中读取一个 64 位整数
  • ReadSingle(): 从当前流中读取一个 32 位浮点数
  • ReadDouble(): 从当前流中读取一个 64 位双精度浮点数
  • ReadString(): 从当前流中读取一个字符串
  • Read([byte[]] buffer, int index, int count): 从当前流中读取一系列字节并将它们存储在指定的缓冲区中
  • PeekChar(): 查看当前流中的下一个字符但不移动读取位置
  • Close(): 关闭当前 BinaryReader 并释放所有相关资源

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System;
using System.IO;

class Program {
static void Main() {
string filePath = "data.bin";
try {
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) {
using (BinaryReader reader = new BinaryReader(fs)) {
int intValue = reader.ReadInt32(); // 读取一个整数
double doubleValue = reader.ReadDouble(); // 读取一个双精度浮点数
string stringValue = reader.ReadString(); // 读取一个字符串

Console.WriteLine($"Integer Value: {intValue}");
Console.WriteLine($"Double Value: {doubleValue}");
Console.WriteLine($"String Value: {stringValue}");
}
}
} catch (Exception ex) {
Console.WriteLine($"Error: {ex.Message}");
}
}
}

BinaryWriter 类

BinaryWriter 类是 C# 中用于将基本数据类型写入流中的类它位于 System.IO 命名空间中BinaryWriter 类提供了一种方便的方式来将各种数据类型如整数浮点数字符串等写入到一个 Stream 中非常适合用于序列化和二进制文件的创建

构造函数

  • BinaryWriter(Stream output): 使用指定的流对象创建 BinaryWriter
  • BinaryWriter(Stream output, Encoding encoding): 使用指定的流对象和编码创建 BinaryWriter
  • BinaryWriter(Stream output, Encoding encoding, bool leaveOpen): 使用指定的流对象编码和一个布尔值来控制是否在 BinaryWriter 关闭时保留基础流的打开状态

属性

  • BaseStream: 获取当前 BinaryWriter 使用的 Stream
  • Encoding: 获取当前 BinaryWriter 使用的 Encoding

方法

  • Write(bool value): 向当前流中写入一个布尔值
  • Write(char value): 向当前流中写入一个字符
  • Write(char[] chars): 向当前流中写入字符数组
  • Write(char[] chars, int index, int count): 向当前流中写入指定索引处开始的字符数组的一部分
  • Write(byte value): 向当前流中写入一个字节
  • Write(sbyte value): 向当前流中写入一个带符号的字节
  • Write(short value): 向当前流中写入一个 16 位整数
  • Write(ushort value): 向当前流中写入一个无符号 16 位整数
  • Write(int value): 向当前流中写入一个 32 位整数
  • Write(uint value): 向当前流中写入一个无符号 32 位整数
  • Write(long value): 向当前流中写入一个 64 位整数
  • Write(ulong value): 向当前流中写入一个无符号 64 位整数
  • Write(float value): 向当前流中写入一个 32 位浮点数
  • Write(double value): 向当前流中写入一个 64 位双精度浮点数
  • Write(decimal value): 向当前流中写入一个十进制数
  • Write(string value): 向当前流中写入一个字符串
  • Write(byte[] array): 向当前流中写入一个字节数组
  • Write(byte[] array, int index, int count): 向当前流中写入指定索引处开始的字节数组的一部分
  • Flush(): 将任何未写入的缓冲数据刷新到流中
  • Close(): 关闭当前 BinaryWriter 并释放所有相关资源

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System;
using System.IO;

class Program {
static void Main() {
string filePath = "data.bin";
try {
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) {
using (BinaryWriter writer = new BinaryWriter(fs)) {
writer.Write(123); // 写入一个整数
writer.Write(3.14); // 写入一个双精度浮点数
writer.Write("Hello, World!"); // 写入一个字符串
}
}
Console.WriteLine("Data written successfully.");
} catch (Exception ex) {
Console.WriteLine($"Error: {ex.Message}");
}
}
}

File 类

File 类是 C# 中用于直接操作文件的类它位于 System.IO 命名空间中File 类提供了一系列静态方法可以用来创建删除复制移动文件以及获取文件的信息等

创建/删除文件

  • Create(string path): 创建一个新文件并返回一个 FileStream 对象用于对该文件进行读写操作
  • CreateText(string path): 创建一个新文件并返回一个 StreamWriter 对象用于向该文件写入文本
  • Delete(string path): 删除指定路径下的文件

文件信息

  • Copy(string sourceFileName, string destFileName): 将指定源文件复制到指定的目标文件
  • Move(string sourceFileName, string destFileName): 移动指定的文件到新的位置并重命名
  • Exists(string path): 检查指定路径下的文件是否存在
  • GetCreationTime(string path): 获取指定文件的创建时间
  • GetLastAccessTime(string path): 获取指定文件的最后访问时间
  • GetLastWriteTime(string path): 获取指定文件的最后写入时间
  • SetCreationTime(string path, DateTime creationTime): 设置指定文件的创建时间
  • SetLastAccessTime(string path, DateTime lastAccessTime): 设置指定文件的最后访问时间
  • SetLastWriteTime(string path, DateTime lastWriteTime): 设置指定文件的最后写入时间

文件读写

  • ReadAllBytes(string path): 将指定文件的所有内容读取到字节数组中
  • ReadAllText(string path): 将指定文件的所有内容读取为字符串
  • WriteAllBytes(string path, byte[] bytes): 将指定的字节数组写入到指定文件中
  • WriteAllText(string path, string contents): 将指定的字符串内容写入到指定文件中
  • AppendAllText(string path, string contents): 将指定的字符串内容追加到指定文件中

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
using System;
using System.IO;

class Program {
static void Main() {
string sourceFilePath = "source.txt";
string destinationFilePath = "destination.txt";

try {
// 创建文件
using (var file = File.Create(sourceFilePath)) {
Console.WriteLine("File created.");
}

// 向文件中写入文本
File.WriteAllText(sourceFilePath, "Hello, this is some content.");

// 读取文件内容
string content = File.ReadAllText(sourceFilePath);
Console.WriteLine("Content read from file: " + content);

// 复制文件
File.Copy(sourceFilePath, destinationFilePath);
Console.WriteLine("File copied.");

// 移动文件
string movedFilePath = "moved.txt";
File.Move(destinationFilePath, movedFilePath);
Console.WriteLine("File moved.");

// 删除文件
File.Delete(movedFilePath);
Console.WriteLine("File deleted.");

// 检查文件是否存在
if (File.Exists(sourceFilePath)) {
Console.WriteLine("Source file still exists.");
}

// 获取文件信息
DateTime creationTime = File.GetCreationTime(sourceFilePath);
Console.WriteLine("Creation time: " + creationTime);

// 设置文件信息
File.SetCreationTime(sourceFilePath, DateTime.Now.AddYears(-1));
Console.WriteLine("Creation time after setting: " + File.GetCreationTime(sourceFilePath));

} catch (Exception ex) {
Console.WriteLine("An error occurred: " + ex.Message);
}
}
}

Path 类

Path 类是 C# 中用于处理文件和目录路径的类它位于 System.IO 命名空间中虽然 Path 类本身没有实例化的方法但它提供了一系列有用的静态方法可以帮助开发者处理路径字符串这些方法可以用于组合路径获取路径的不同部分验证路径的有效性等

属性

  • DirectorySeparatorChar: 获取当前操作系统使用的目录分隔符通常是 \ 或 /
  • AltDirectorySeparatorChar: 获取替代的目录分隔符在 Windows 上通常是 /
  • VolumeSeparatorChar: 获取卷分隔符通常是 :
  • PathSeparator: 获取路径中的元素分隔符通常用于环境变量中的路径列表例如 ;
  • InvalidPathChars: 获取无效的路径字符集合
  • InvalidFileNameChars: 获取无效的文件名字符集合
  • Extension: 获取文件扩展名当使用 Path 类的 ChangeExtension 方法时有用
  • FullPath: 获取完全限定的路径名称

方法

  • Combine(string path1, string path2): 将两个路径组合成一个完整的路径
  • GetDirectoryName(string path): 获取指定路径的目录名称部分
  • GetFileName(string path): 获取指定路径的文件名称部分
  • GetFileNameWithoutExtension(string path): 获取指定路径的文件名称部分不包括扩展名
  • GetExtension(string path): 获取指定路径的文件扩展名
  • GetFullPath(string path): 获取指定路径的完全限定形式
  • GetTempPath(): 获取系统的临时目录路径
  • GetRandomFileName(): 生成一个随机的文件名
  • HasExtension(string path): 检查指定路径是否有扩展名
  • ChangeExtension(string path, string extension): 更改指定路径的文件扩展名
  • GetPathRoot(string path): 获取指定路径的根目录部分
  • IsPathRooted(string path): 检查指定路径是否是绝对路径

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
using System;
using System.IO;

class Program {
static void Main() {
string directory = "C:\\Users\\ExampleUser\\Documents";
string fileName = "example.txt";

// 组合目录和文件名
string fullPath = Path.Combine(directory, fileName);
Console.WriteLine("Full Path: " + fullPath);

// 获取文件名
string justFileName = Path.GetFileName(fullPath);
Console.WriteLine("File Name: " + justFileName);

// 获取文件名不含扩展名的部分
string fileNameNoExt = Path.GetFileNameWithoutExtension(fullPath);
Console.WriteLine("File Name Without Extension: " + fileNameNoExt);

// 获取文件扩展名
string fileExtension = Path.GetExtension(fullPath);
Console.WriteLine("File Extension: " + fileExtension);

// 更改文件扩展名
string newFullPath = Path.ChangeExtension(fullPath, ".bak");
Console.WriteLine("New Full Path with Changed Extension: " + newFullPath);

// 获取目录名
string dirName = Path.GetDirectoryName(fullPath);
Console.WriteLine("Directory Name: " + dirName);

// 获取系统临时目录路径
string tempPath = Path.GetTempPath();
Console.WriteLine("Temporary Directory Path: " + tempPath);

// 获取随机文件名
string randomFileName = Path.GetRandomFileName();
Console.WriteLine("Random File Name: " + randomFileName);
}
}

Regex 类

在 C# 中System.Text.RegularExpressions 命名空间提供了 Regex 类用于处理正则表达式相关的操作正则表达式是一种强大的文本匹配工具用于查找字符串中的模式

创建对象

1
2
3
4
5
6
7
8
9
using System;
using System.Text.RegularExpressions;

class Program {
static void Main() {
// 创建 Regex 对象
Regex regex = new Regex(@"\d+"); // 匹配一个或多个数字
}
}

RegexOptions

RegexOptions 枚举包含了多种选项用于控制正则表达式的匹配行为这些选项可以在创建 Regex 对象时通过构造函数的第二个参数传递或者在调用某些静态方法时作为参数传递

  • IgnoreCase忽略大小写差异在匹配时不区分字母的大小写
  • Multiline处理多行文本使得 ^ 和 $ 能够匹配每一行的开始和结束而不仅仅是整个字符串的开始和结束
  • Singleline改变 . 的行为使其匹配任何字符包括换行符
  • Compiled创建预编译的正则表达式提高多次匹配的效率
  • ExplicitCapture默认情况下所有的括号捕获都会被记录下来使用此选项后只有显式标记为捕获的括号才会记录
  • ECMAScript使正则表达式的语法符合 ECMAScript 标准这个选项很少使用通常用于兼容性
  • RightToLeft从右到左执行匹配这通常用于处理从右到左书写的语言如阿拉伯语
  • IgnoreCase | Multiline可以组合多个选项使用按位或运算符 | 来指定多个选项
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System;
using System.Text.RegularExpressions;

class Program {
static void Main() {
string text = "Start of line\nAnother line\nEnd of text";
string pattern = @"^\w+";

// 不使用 Multiline 选项
Match match = Regex.Match(text, pattern);
if (match.Success) {
Console.WriteLine($"Match without Multiline: {match.Value}");
} else {
Console.WriteLine("No match without Multiline.");
}

// 使用 Multiline 选项
match = Regex.Match(text, pattern, RegexOptions.Multiline);
if (match.Success) {
Console.WriteLine($"First match with Multiline: {match.Value}");
}

// 使用 IgnoreCase 选项
string textIgnoreCase = "The Word Was Found.";
pattern = @"\bword\b";
match = Regex.Match(textIgnoreCase, pattern, RegexOptions.IgnoreCase);
if (match.Success) {
Console.WriteLine($"IgnoreCase match: {match.Value}");
}
}
}

静态方法

检查整个输入字符串是否与模式匹配

1
2
bool isMatch = Regex.IsMatch("123abc", @"\d+");
Console.WriteLine(isMatch); // 输出True

获取第一个匹配项

1
2
3
4
Match match = Regex.Match("123abc", @"\d+");
if (match.Success) {
Console.WriteLine(match.Value); // 输出123
}

获取所有匹配项

1
2
3
4
MatchCollection matches = Regex.Matches("123 abc 456 def 789", @"\d+");
foreach (Match m in matches) {
Console.WriteLine(m.Value); // 输出123 456 789
}

替换所有匹配的子串

1
2
string result = Regex.Replace("123abc", @"\d+", "#");
Console.WriteLine(result); // 输出#abc

解码正则表达式中的转义序列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System;
using System.Text.RegularExpressions;

class Program {
static void Main() {
string escapedPattern = @"\.";
string unescapedPattern = Regex.Unescape(escapedPattern);
Console.WriteLine($"Escaped Pattern: {escapedPattern} -> Unescaped Pattern: {unescapedPattern}");

// 更复杂的例子
string complexPattern = @"\d+\.\d+"; // 匹配形如 "123.456" 的字符串
string unescapedComplexPattern = Regex.Unescape(complexPattern);
Console.WriteLine($"Escaped Complex Pattern: {complexPattern} -> Unescaped Pattern: {unescapedComplexPattern}");

// 检查是否正确解码
string testString = "123.456";
bool isMatch = Regex.IsMatch(testString, unescapedComplexPattern);
Console.WriteLine($"Is the test string a match? {isMatch}");
}
}

Json 处理类

System.Text.Json 是 .NET Core 3.0 和 .NET 5+ 中引入的一个新的 JSON 序列化器它更加轻量级且性能更高

将对象转换为 JSON 字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using System;
using System.Text.Json;

public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}

class Program
{
static void Main()
{
Person person = new Person { Name = "Alice", Age = 30 };

string jsonString = JsonSerializer.Serialize(person);
Console.WriteLine(jsonString);
}
}

将 JSON 字符串转换为对象

1
2
3
4
5
6
7
8
9
10
class Program
{
static void Main()
{
string jsonString = "{\"Name\":\"Bob\",\"Age\":40}";

Person person = JsonSerializer.Deserialize<Person>(jsonString);
Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");
}
}

JsonUtility 类

JsonUtility 是 Unity 引擎中用于序列化与反序列化 JSON 数据的一个工具类它不是 .NET Framework 或 C# 语言的一部分而是 Unity 特有的功能

将对象转换为 JSON 字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using UnityEngine;

public class Example : MonoBehaviour {
void Start() {
var myObject = new MyDataClass() { name = "John", age = 30, city = "New York" };
string jsonString = JsonUtility.ToJson(myObject);
Debug.Log(jsonString);
}
}

[System.Serializable]
public class MyDataClass {
public string name;
public int age;
public string city;
}

将 JSON 字符串转换为对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using UnityEngine;

public class Example : MonoBehaviour {
void Start() {
string jsonString = "{\"name\":\"John\",\"age\":30,\"city\":\"New York\"}";
MyDataClass deserializedObject = JsonUtility.FromJson<MyDataClass>(jsonString);
Debug.Log(deserializedObject.name);
}
}

[System.Serializable]
public class MyDataClass {
public string name;
public int age;
public string city;
}

桌面窗口

Windows Forms通常简称为 WinForms是 Microsoft .NET Framework 中的一部分用于创建桌面应用程序的用户界面它提供了丰富的控件集以及易于使用的编程模型来帮助开发者快速构建应用程序

创建应用程序

  • 启动 Visual Studio
  • 创建新项目选择文件 > 新建 > 项目
  • 选择模板选择Windows Forms App (.NET)模板
  • 命名项目给你的项目起个名字比如 MyFirstWinFormsApp
  • 创建项目点击创建按钮
  • Visual Studio 将会为你创建一个带有基本窗体的新项目

定制窗体

属性描述
StartPosition 此属性用于设置窗体启动时的位置它接受一个枚举值 FormStartPosition
Location 当 StartPosition 设置为 Manual 时此属性用于设置窗体左上角的坐标
Size 此属性用于设置窗体的宽度和高度
Text 此属性用于设置窗体标题栏中的文本
Icon 此属性用于设置窗体的图标可以通过 System.Drawing.Icon 类型的对象设置
BackColor 此属性用于设置窗体背景颜色
ForeColor 此属性用于设置窗体前景颜色通常是指字体颜色
Visible 此属性用于控制窗体是否可见默认值为 true
Enabled 此属性用于控制窗体是否启用如果设置为 false则窗体上的所有控件也将被禁用
Cursor 此属性用于设置窗体的鼠标光标样式可以设置为 System.Windows.Forms.Cursors 枚举中的任何值
MaximizeBox 此属性用于控制窗体是否允许最大化如果设置为 false则不会显示最大化按钮
MinimizeBox 此属性用于控制窗体是否允许最小化如果设置为 false则不会显示最小化按钮
FormBorderStyle 此属性用于设置窗体边框样式可以设置为 FormBorderStyle 枚举中的不同值如 None, FixedSingle, Sizable 等
FormClosing 此事件发生于窗体关闭之前可以在此事件处理程序中执行一些清理操作或取消关闭操作
ClientSize 此属性返回窗体客户区的大小不包括标题栏和边框可以用来调整客户区的大小
AutoScaleMode 此属性用于设置窗体的自动缩放模式可以选择基于 DPI字体或其他因素自动缩放控件
Controls 此属性返回窗体上所有控件的集合可以使用此属性来遍历或修改控件
Name 此属性用于设置窗体的名称在代码中引用窗体时很有用
Handle 此属性返回窗体的句柄HWND这个句柄可以用来直接与底层操作系统交互
IsMdiContainer 此属性用于控制窗体是否作为多文档界面MDI容器
TopMost 此属性用于控制窗体是否始终位于其他所有窗口之上
Opacity 此属性用于设置窗体的透明度范围从 0.0 到 1.0
Padding 此属性用于设置窗体客户区的内边距
Font 此属性用于设置窗体上的字体
HelpButton 此属性用于控制是否在窗体的标题栏上显示帮助按钮
HelpButtonClicked 此事件发生于帮助按钮被点击时
ShowInTaskbar 此属性用于控制窗体是否出现在任务栏上
WindowState 此属性用于设置窗体的状态正常最小化或最大化

使用控件

TextBox 控件

TextBox 控件用于输入文本你可以通过拖拽控件到窗体上来添加它

  • 打开工具箱在解决方案资源管理器旁边可以看到工具箱窗口
  • 拖拽 TextBox从工具箱中拖拽 TextBox 控件到窗体上

Button 控件

Button 控件用于触发事件

  • 拖拽 Button从工具箱中拖拽 Button 控件到窗体上
  • 设置 Text 属性设置按钮上的文本例如 button1.Text = “Click Me!”

Label 控件

Label 控件用于显示静态文本

  • 拖拽 Label从工具箱中拖拽 Label 控件到窗体上
  • 设置 Text 属性设置标签上的文本例如 label1.Text = “Welcome to WinForms!”

ListBox 控件

ListBox 控件用于显示列表

  • 拖拽 ListBox从工具箱中拖拽 ListBox 控件到窗体上
  • 添加项目可以通过代码添加项目
1
2
listBox1.Items.Add("Item 1");
listBox1.Items.Add("Item 2");

ComboBox 控件

ComboBox 控件用于显示下拉列表

  • 拖拽 ComboBox从工具箱中拖拽 ComboBox 控件到窗体上
  • 添加项目可以通过代码添加项目
1
2
comboBox1.Items.Add("Option 1");
comboBox1.Items.Add("Option 2");

DataGridView 控件

DataGridView 控件用于显示表格数据

  • 拖拽 DataGridView从工具箱中拖拽 DataGridView 控件到窗体上
  • 绑定数据使用 BindingSource 绑定数据
1
2
3
4
5
6
7
8
DataTable table = new DataTable();
table.Columns.Add("Name", typeof(string));
table.Rows.Add("John Doe");
table.Rows.Add("Jane Doe");

BindingSource bindingSource = new BindingSource();
bindingSource.DataSource = table;
dataGridView1.DataSource = bindingSource;

TabControl 控件

TabControl 控件用于管理多个选项卡页面

  • 拖拽 TabControl从工具箱中拖拽 TabControl 控件到窗体上
  • 添加 TabPage可以通过代码添加 TabPage
1
2
3
TabPage tabPage1 = new TabPage("Tab 1");
tabPage1.Controls.Add(new Button { Text = "Button in Tab 1" });
tabControl1.TabPages.Add(tabPage1);

ListView 控件

ListView 控件用于显示图标列表或详细视图中的项目集合

  • 拖拽 ListView从工具箱中拖拽 ListView 控件到窗体上
  • 添加列通过代码添加列
1
2
3
listView1.View = View.Details;
listView1.Columns.Add("Column 1");
listView1.Columns.Add("Column 2");

WebBrowser 控件

WebBrowser 控件用于显示网页内容

  • 拖拽 WebBrowser从工具箱中拖拽 WebBrowser 控件到窗体上
  • 加载网页可以通过代码加载网页
1
webBrowser1.Navigate("http://www.example.com");

ErrorProvider 控件

ErrorProvider 控件用于提供错误指示器

  • 拖拽 ErrorProvider从工具箱中拖拽 ErrorProvider 控件到窗体上
  • 设置错误可以通过代码设置错误
1
errorProvider1.SetError(textBox1, "This field is required.");

OpenFileDialog 控件

OpenFileDialog 用于让用户选择一个或多个文件

  • 拖拽 Dialogs从工具箱中拖拽 OpenFileDialog 控件到窗体上
  • 显示对话框可以通过代码显示对话框
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private void buttonOpen_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();

// 设置对话框的属性
openFileDialog.Title = "Select File";
openFileDialog.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
openFileDialog.InitialDirectory = @"C:\";
openFileDialog.Multiselect = true; // 允许选择多个文件

// 显示对话框
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
// 处理用户选择的文件
foreach (string filename in openFileDialog.FileNames)
{
MessageBox.Show($"Selected file: {filename}");
}
}
}

SaveFileDialog 控件

SaveFileDialog 用于让用户指定文件的保存位置和名称

  • 拖拽 Dialogs从工具箱中拖拽 SaveFileDialog 控件到窗体上
  • 显示对话框可以通过代码显示对话框
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private void buttonSave_Click(object sender, EventArgs e)
{
SaveFileDialog saveFileDialog = new SaveFileDialog();

// 设置对话框的属性
saveFileDialog.Title = "Save As";
saveFileDialog.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
saveFileDialog.InitialDirectory = @"C:\";

// 显示对话框
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
// 处理用户指定的文件名
string fileName = saveFileDialog.FileName;
MessageBox.Show($"File will be saved as: {fileName}");

// 保存文件的逻辑
using (StreamWriter writer = new StreamWriter(fileName))
{
writer.WriteLine("Hello, world!");
}
}
}

数据绑定

WinForms 支持数据绑定这意味着你可以轻松地将控件与数据源如数据库连接起来

  • 创建数据源例如使用 DataTable 或 BindingList 类型
  • 绑定控件将控件绑定到数据源
1
2
3
4
5
6
7
8
DataTable data = new DataTable();
data.Columns.Add("Name", typeof(string));
data.Rows.Add("Alice");
data.Rows.Add("Bob");

BindingSource bindingSource = new BindingSource();
bindingSource.DataSource = data;
dataGridView1.DataSource = bindingSource;

事件处理

WinForms 控件可以通过事件处理程序响应用户的操作

  • 双击控件在设计视图中双击控件Visual Studio 会自动创建一个事件处理程序
  • 编写事件处理程序
1
2
3
4
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Button clicked!");
}

窗体间通信

如果你的应用程序包含多个窗体你可能需要在这些窗体之间传递数据

  • 使用属性在窗体类中定义公共属性
  • 传递数据
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public partial class Form1 : Form
    {
    public string SharedData { get; set; }

    public Form1()
    {
    InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
    SharedData = "Hello from Form1!";
    Form2 form2 = new Form2();
    form2.Show();
    }
    }

    public partial class Form2 : Form
    {
    public Form2()
    {
    InitializeComponent();
    }

    private void Form2_Load(object sender, EventArgs e)
    {
    MessageBox.Show($"Data from Form1: {((Form1)Owner).SharedData}");
    }
    }

MDI 窗体

多文档界面 (MDI) 允许在一个主窗体中打开多个子窗体

  • 设置窗体属性将主窗体的 IsMdiContainer 属性设置为 true
  • 打开子窗体
1
2
3
4
5
6
7
private void menuStripFileNew_Click(object sender, EventArgs e)
{
Form childForm = new Form();
childForm.MdiParent = this;
childForm.Text = "Window";
childForm.Show();
}

文件对话框

你可以使用 OpenFileDialog 和 SaveFileDialog 来让用户选择文件或指定保存位置

  • 创建对话框实例化 OpenFileDialog 或 SaveFileDialog
  • 显示对话框
1
2
3
4
5
6
7
8
9
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
MessageBox.Show($"Selected file: {openFileDialog.FileName}");
}
}

GDI 绘图

使用GDI+Graphics Device Interface Plus进行绘图是一种常见的技术GDI+是Windows操作系统提供的用于绘制图形和处理图像的一个API集合

Graphics 类

  • 用途Graphics 类是 GDI+ 的核心它提供了所有绘制图形的基本方法
  • 方法
    • DrawLine: 用于绘制线段
    • DrawRectangle, DrawEllipse, DrawPolygon: 用于绘制矩形椭圆和多边形
    • FillRectangle, FillEllipse, FillPolygon: 用于填充矩形椭圆和多边形
    • DrawImage: 用于绘制图像
    • DrawString: 用于绘制文本
  • 简单示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    using System.Drawing;
    using System.Drawing.Drawing2D;

    public void DrawOnGraphics()
    {
    using (Bitmap bitmap = new Bitmap(100, 100))
    {
    using (Graphics graphics = Graphics.FromImage(bitmap))
    {
    graphics.Clear(Color.White);
    using (Pen pen = new Pen(Color.Black))
    {
    graphics.DrawRectangle(pen, 10, 10, 80, 80);
    }
    }

    // 显示或保存 bitmap
    bitmap.Save("rectangle.png");
    }
    }

Pen 类

  • 用途Pen 类用于定义线条的颜色和宽度等属性
  • 属性
    • Color: 设置线条颜色
    • Width: 设置线条宽度
    • DashStyle: 设置线条的虚线样式
    • StartCap 和 EndCap: 设置线段两端的样式
    • LineJoin: 设置线段之间的连接样式
  • 简单示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    using System.Drawing;

    public void UsePen()
    {
    using (Bitmap bitmap = new Bitmap(100, 100))
    {
    using (Graphics graphics = Graphics.FromImage(bitmap))
    {
    using (Pen pen = new Pen(Color.Blue, 5))
    {
    pen.DashStyle = DashStyle.DashDot;
    graphics.DrawRectangle(pen, 10, 10, 80, 80);
    }
    }

    bitmap.Save("dashed_rectangle.png");
    }
    }

Brush 类

  • 用途Brush 类用于填充形状的颜色和图案
  • 派生类
    • SolidBrush: 用于纯色填充
    • HatchBrush: 用于带有图案的填充
    • TextureBrush: 用于使用图像纹理进行填充
    • LinearGradientBrush: 用于使用线性渐变进行填充
    • PathGradientBrush: 用于使用路径渐变进行填充
  • 简单示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    using System.Drawing;

    public void UseBrush()
    {
    using (Bitmap bitmap = new Bitmap(100, 100))
    {
    using (Graphics graphics = Graphics.FromImage(bitmap))
    {
    using (SolidBrush brush = new SolidBrush(Color.Red))
    {
    graphics.FillRectangle(brush, 10, 10, 80, 80);
    }
    }

    bitmap.Save("filled_rectangle.png");
    }
    }

GraphicsPath 类

  • 用途GraphicsPath 类用于定义和操作复杂的路径
  • 方法
    • AddLine, AddArc, AddBezier, AddCurve, AddEllipse, AddPolygon: 用于向路径中添加不同的图形元素
    • CloseFigure: 用于关闭路径
    • Reset: 用于清除路径中的所有图形
    • GetBounds: 用于获取路径的边界
    • GetPoints: 用于获取路径中点的信息
    • Flatten: 用于将路径中的曲线扁平化成直线段
  • 简单示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    using System.Drawing;
    using System.Drawing.Drawing2D;

    public void UseGraphicsPath()
    {
    using (Bitmap bitmap = new Bitmap(100, 100))
    {
    using (Graphics graphics = Graphics.FromImage(bitmap))
    {
    using (GraphicsPath path = new GraphicsPath())
    {
    path.AddRectangle(new Rectangle(10, 10, 80, 80));
    using (Pen pen = new Pen(Color.Black))
    {
    graphics.DrawPath(pen, path);
    }
    }
    }

    bitmap.Save("path_rectangle.png");
    }
    }

Font 类

  • 用途Font 类用于定义文本的字体样式
  • 属性
    • Family: 字体家族名称
    • Size: 字体大小
    • Style: 字体样式例如粗体斜体等
    • Unit: 字体单位例如点数像素等
  • 简单示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    using System.Drawing;

    public void UseFont()
    {
    using (Bitmap bitmap = new Bitmap(100, 100))
    {
    using (Graphics graphics = Graphics.FromImage(bitmap))
    {
    using (Font font = new Font("Arial", 12))
    {
    using (SolidBrush brush = new SolidBrush(Color.Black))
    {
    graphics.DrawString("Hello", font, brush, 10, 10);
    }
    }
    }

    bitmap.Save("text.png");
    }
    }

StringFormat 类

  • 用途StringFormat 类用于控制文本的排列方式
  • 属性
    • Alignment: 文本的水平对齐方式
    • LineAlignment: 文本的垂直对齐方式
    • Trimming: 当文本超出指定区域时的截断行为
    • HotkeyPrefix: 控制热键前缀的行为
  • 简单示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    using System.Drawing;
    using System.Drawing.Text;

    public void UseStringFormat()
    {
    using (Bitmap bitmap = new Bitmap(100, 100))
    {
    using (Graphics graphics = Graphics.FromImage(bitmap))
    {
    using (Font font = new Font("Arial", 12))
    {
    using (SolidBrush brush = new SolidBrush(Color.Black))
    {
    StringFormat format = new StringFormat();
    format.Alignment = StringAlignment.Center;
    format.LineAlignment = StringAlignment.Center;
    graphics.DrawString("Hello", font, brush, new Rectangle(0, 0, 100, 100), format);
    }
    }
    }

    bitmap.Save("centered_text.png");
    }
    }

Image 类

  • 用途Image 类用于加载和操作图像
  • 方法
    • Save: 保存图像到文件
    • GetThumbnailImage: 获取图像的缩略图
    • Clone: 复制图像
  • 简单示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    using System.Drawing;

    public void UseImage()
    {
    using (Bitmap bitmap = new Bitmap(100, 100))
    {
    using (Graphics graphics = Graphics.FromImage(bitmap))
    {
    using (Image image = Image.FromFile("example.jpg"))
    {
    graphics.DrawImage(image, 0, 0, 100, 100);
    }
    }

    bitmap.Save("drawn_image.png");
    }
    }

Bitmap 类

  • 用途Bitmap 类用于在内存中创建和编辑位图图像
  • 方法
    • SetPixel, GetPixel: 设置和获取图像中某个像素的颜色值
    • LockBits, UnlockBits: 锁定和解锁位图以便直接访问像素数据
  • 简单示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    using System.Drawing;

    public void UseBitmap()
    {
    using (Bitmap bitmap = new Bitmap(100, 100))
    {
    using (Graphics graphics = Graphics.FromImage(bitmap))
    {
    graphics.Clear(Color.White);
    using (Pen pen = new Pen(Color.Black))
    {
    graphics.DrawRectangle(pen, 10, 10, 80, 80);
    }
    }

    bitmap.Save("bitmap_rectangle.png");
    }
    }

GraphicsContainer 类

  • 用途GraphicsContainer 类用于保存当前Graphics对象的状态这样就可以在进行一系列变换后恢复到之前的状态
  • 方法
    • BeginContainer: 开始一个容器保存当前状态
    • EndContainer: 结束一个容器恢复之前的状态
  • 简单示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    using System.Drawing;

    public void UseGraphicsContainer()
    {
    using (Bitmap bitmap = new Bitmap(100, 100))
    {
    using (Graphics graphics = Graphics.FromImage(bitmap))
    {
    using (Pen pen = new Pen(Color.Black))
    {
    using (var container = graphics.BeginContainer())
    {
    graphics.TranslateTransform(50, 50);
    graphics.RotateTransform(45);
    graphics.DrawRectangle(pen, -40, -40, 80, 80);
    }
    }
    }

    bitmap.Save("transformed_rectangle.png");
    }
    }

Matrix 类

  • 用途Matrix 类用于定义二维仿射变换
  • 方法
    • Translate, Scale, Rotate: 分别用于平移缩放和旋转变换
    • Multiply: 用于组合多个变换
  • 简单示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    using System.Drawing;
    using System.Drawing.Drawing2D;

    public void UseMatrix()
    {
    using (Bitmap bitmap = new Bitmap(100, 100))
    {
    using (Graphics graphics = Graphics.FromImage(bitmap))
    {
    using (Pen pen = new Pen(Color.Black))
    {
    Matrix matrix = new Matrix();
    matrix.Translate(50, 50);
    matrix.Rotate(45);
    graphics.Transform = matrix;
    graphics.DrawRectangle(pen, -40, -40, 80, 80);
    }
    }

    bitmap.Save("matrix_transformed_rectangle.png");
    }
    }

Region 类

  • 用途Region 类用于定义一个不规则的区域
  • 方法
    • Union: 合并两个区域
    • Intersect: 计算两个区域的交集
    • Complement: 计算一个区域的补集
  • 简单示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    using System.Drawing;
    using System.Drawing.Drawing2D;

    public void UseRegion()
    {
    using (Bitmap bitmap = new Bitmap(100, 100))
    {
    using (Graphics graphics = Graphics.FromImage(bitmap))
    {
    using (Region region = new Region(new Rectangle(10, 10, 80, 80)))
    {
    graphics.SetClip(region);
    using (SolidBrush brush = new SolidBrush(Color.Red))
    {
    graphics.FillRectangle(brush, 0, 0, 100, 100);
    }
    }
    }

    bitmap.Save("clipped_rectangle.png");
    }
    }

网络通信

你可以使用诸如 WebSocket 或 TCP 客户端等技术实现远程控制和数据交换

  • 设置客户端例如使用 TcpClient 类建立 TCP 连接
  • 发送和接收数据使用 NetworkStream 对象进行数据传输

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

class Server
{
static void Main()
{
TcpListener serverSocket = null;
try
{
Int32 port = 13000;
IPAddress localAddr = IPAddress.Parse("127.0.0.1");

// TcpListener 服务器端
serverSocket = new TcpListener(localAddr, port);

serverSocket.Start();

Console.WriteLine("Waiting for a connection...");

// 等待客户端连接
TcpClient client = serverSocket.AcceptTcpClient();

Console.WriteLine("Connected by: " + client.Client.RemoteEndPoint.ToString());

// 获取网络流
NetworkStream stream = client.GetStream();

// 发送数据
String data = "Welcome to the server!";
Byte[] outStream = Encoding.ASCII.GetBytes(data);
stream.Write(outStream, 0, outStream.Length);

// 接收数据
int bytes = 0;
Byte[] inStream = new Byte[256];
bytes = stream.Read(inStream, 0, inStream.Length);
data = Encoding.ASCII.GetString(inStream, 0, bytes);
Console.WriteLine("Received: {0}", data);

// 关闭连接
stream.Close();
client.Close();
serverSocket.Stop();
}
catch (Exception e)
{
Console.WriteLine("Could not listen on port: " + e.Message);
}
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using System;
using System.Net.Sockets;
using System.Text;

class Client
{
static void Main(string[] args)
{
String dataFromServer = "";
String dataFromUser = "";

TcpClient client = new TcpClient("127.0.0.1", 13000);

// 获取网络流
NetworkStream stream = client.GetStream();

// 发送数据
dataFromUser = "Hello from the client!";
Byte[] outStream = Encoding.ASCII.GetBytes(dataFromUser);
stream.Write(outStream, 0, outStream.Length);

// 接收数据
Byte[] inStream = new Byte[256];
int bytes = stream.Read(inStream, 0, inStream.Length);
dataFromServer = Encoding.ASCII.GetString(inStream, 0, bytes);
Console.WriteLine("Server says: {0}", dataFromServer);

// 关闭连接
stream.Close();
client.Close();
}
}

坦克大战

💗💗 下边将要运用所学习到的知识开发出经典的坦克大战游戏

创建项目

⭐⭐ 创建桌面窗口项目

  • 启动 Visual Studio
  • 创建新项目选择文件 > 新建 > 项目
  • 选择模板Windows窗体应用(.NET Framework) 模板
  • 项目名称TankGreatWar
  • 解决方案WinFormsApp
  • .NET模版.NET Framework 4.7.2
  • 创建项目点击创建按钮
  • Visual Studio 将会为你创建一个带有基本窗体的新项目

⭐⭐ 游戏代码运行逻辑

  • 游戏入口Program.Main() 方法
  • 初始化游戏窗口Application.Run(new Form1());
  • Form1 类就是游戏窗口类所有的游戏操作方法都是在这里调用的

⭐⭐ 添加游戏所需资源

  • 下载坦克大战所需图片资源音频资源等下载链接: https://pan.baidu.com/s/1gBhJzeWTJNyDb_lPN5jmxw?pwd=ntvk 提取码: ntvk
  • 打开 Properties 下边的 Resources.resx 文件点击添加资源的下拉按钮选择添加现有文件选项
  • 添加坦克大战的图片资源和音频资源

窗口类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
partial class Form1
{
/// <summary>
/// 必需的设计器变量
/// </summary>
private System.ComponentModel.IContainer components = null;

/// <summary>
/// 清理所有正在使用的资源
/// </summary>
/// <param name="disposing">如果应释放托管资源为 true否则为 false</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}

#region Windows 窗体设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容
/// </summary>
private void InitializeComponent()
{
this.SuspendLayout();
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
//修改窗口名称和宽高
this.ClientSize = new System.Drawing.Size(450, 450);
this.Name = "Form1";
this.Text = "坦克大战";
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.Form1_FormClosed);
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);
this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyUp);
this.ResumeLayout(false);

}
#endregion
}

public partial class Form1 : Form
{
private Graphics graphics; //窗口画布

private Thread thread; //游戏框架线程

GameFramework gameFramework; //游戏主框架

public Form1()
{
//创建窗体上的所有控件实例并为它们分配默认的属性值VS自动生成
InitializeComponent();

//设置窗体位置
this.StartPosition = FormStartPosition.CenterScreen;

//禁止调整窗体大小
this.FormBorderStyle = FormBorderStyle.FixedSingle;

//设置窗口背景色
this.BackColor = Color.Black;

//获取画布
graphics = this.CreateGraphics();

//开启子线程
gameFramework = new GameFramework(graphics);
thread = new Thread(new ThreadStart(gameFramework.GameMainThread));
thread.Start();
}

//窗口关闭行为
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
//关闭子线程
thread.Abort();
}

//键盘按下事件
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
gameFramework.KeyDown(e);
}

//键盘抬起事件
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
gameFramework.KeyUp(e);
}
}

主框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/// <summary>
/// 游戏框架
/// </summary>
public class GameFramework
{

/// <summary>
/// 刷新时间每秒刷新60次
/// </summary>
const int SleepTime = 1000 / 60;

/// <summary>
/// 窗口画布
/// </summary>
private readonly Graphics Graphics;

/// <summary>
/// 游戏页面图片
/// </summary>
public Bitmap Bitmap;

/// <summary>
/// 游戏页面图片画布
/// </summary>
public Graphics BitmapGraphics;

/// <summary>
/// 构造函数
/// </summary>
/// <param name="graphics">Window Forms 窗口画布</param>
public GameFramework(Graphics graphics)
{
this.Graphics = graphics;
//创建一个游戏图片画布
Bitmap = new Bitmap(450, 450);
BitmapGraphics = Graphics.FromImage(Bitmap);
//设置游戏页面图片背景色为黑色
BitmapGraphics.Clear(Color.Black);
}


/// <summary>
/// 游戏主线程
/// </summary>
public void GameMainThread()
{
Init();
while (true)
{
Update();
Thread.Sleep(SleepTime);
}
}

/// <summary>
/// 游戏初始化
/// </summary>
private void Init()
{
//初始化音效
SoundManager.Init();
//初始化墙
WallManager.Init(BitmapGraphics);
//初始化Boss
BossManager.Init(BitmapGraphics);
//初始化坦克
TankManager.Init(BitmapGraphics);
//初始化子弹
BulletManager.Init();
//初始化爆炸效果
ExplosionManager.Init();
//游戏开始音效
SoundManager.PlayStart();
}

/// <summary>
/// 游戏窗口刷新
/// </summary>
private void Update()
{
if (BossManager.IsGameOver)
{
//游戏结束
BossManager.GameOver(BitmapGraphics);
}
else {
// 刷新游戏图片
RefreshBitmap();
}
// 绘制 Window Forms 窗口
DrawWinForm();
}

/// <summary>
/// 刷新游戏图片
/// </summary>
private void RefreshBitmap()
{
//设置游戏页面图片背景色为黑色
BitmapGraphics.Clear(Color.Black);

//绘制墙
WallManager.DrawWall();

//绘制BOSS
BossManager.DrawBoss();

//绘制我的坦克
TankManager.DrawMyTank();

//绘制敌人的坦克
TankManager.DrawEnemyTank();

//绘制子弹
BulletManager.DrawBullet();

//绘制爆炸效果
ExplosionManager.DrawExplosion();
}

/// <summary>
/// 绘制 Window Forms 窗口
/// </summary>
private void DrawWinForm()
{
Graphics.DrawImage(Bitmap, 0, 0);
}

//按键是否按下
private bool IsSpaceKeyPressed = false;

/// <summary>
/// 键盘按下事件
/// </summary>
/// <param name="e"></param>
public void KeyDown(KeyEventArgs e)
{
//游戏结束后的键盘事件
if (BossManager.IsGameOver)
{
//按回车重新开始
if (e.KeyCode == Keys.Enter)
{
Init();
BossManager.IsGameOver = false;
}
}
//游戏中的键盘事件
else
{
//上下左右移动
if (e.KeyCode == Keys.Up || e.KeyCode == Keys.Down || e.KeyCode == Keys.Left || e.KeyCode == Keys.Right)
{
TankManager.KeyDown(e);
}
//发射子弹//isEnterKeyPressed处理持续按下的情况
else if (e.KeyCode == Keys.Space && !IsSpaceKeyPressed)
{
BulletManager.CreateMyTankBullet();
IsSpaceKeyPressed = true;
}
}
}

/// <summary>
/// 键盘抬起事件
/// </summary>
/// <param name="e"></param>
public void KeyUp(KeyEventArgs e)
{
if (e.KeyCode == Keys.Space)
{
IsSpaceKeyPressed = false;
}
}
}

实体类

父类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
abstract class GameObject
{
/// <summary>
/// 窗口 X 坐标
/// </summary>
public int X { get; set; }

/// <summary>
/// 窗口 Y 坐标
/// </summary>
public int Y { get; set; }

/// <summary>
/// 画布
/// </summary>
public Graphics Graphics { get; set; }

/// <summary>
/// 获取图片
/// </summary>
/// <returns></returns>
public abstract Image GetImage();

/// <summary>
/// 获取自身的二维矩形
/// </summary>
/// <returns></returns>
public abstract Rectangle GetRectangle();

/// <summary>
/// 画出自身位置
/// </summary>
public virtual void DrawSelf()
{
lock (Graphics) {
// ? 检查 Graphics 是否为空不为空则执行 DrawImage 方法
Graphics?.DrawImage(GetImage(), this.X, this.Y);
}
}

/// <summary>
/// 有参构造方法
/// </summary>
/// <param name="x"> 窗口 X 坐标 </param>
/// <param name="y"> 窗口 Y 坐标 </param>
/// <param name="graphics"> 画布 </param>
public GameObject(int x, int y, Graphics graphics)
{
this.X = x;
this.Y = y;
this.Graphics = graphics;
}
}

枚举类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/// <summary>
/// 物体朝向枚举类
/// </summary>
enum Direction
{
/// <summary>
/// 物体朝上
/// </summary>
Up,

/// <summary>
/// 物体朝下
/// </summary>
Down,

/// <summary>
/// 物体朝左
/// </summary>
Left,

/// <summary>
/// 物体朝右
/// </summary>
Right
}

/// <summary>
/// 子弹是哪种坦克发射的
/// </summary>
enum BulletTank {

/// <summary>
/// 我的坦克发射的子弹
/// </summary>
MyTank,

/// <summary>
/// 敌人的坦克发射的子弹
/// </summary>
EnemyTank
}

坦克类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
/// <summary>
/// 坦克类
/// </summary>
abstract class Tank : GameObject
{
/// <summary>
/// 物体朝向
/// </summary>
public Direction Direction { get; set; }

/// <summary>
/// 坦克向上的图片
/// </summary>
public Bitmap BitmapUp { get; set; }

/// <summary>
/// 坦克向下的图片
/// </summary>
public Bitmap BitmapDown { get; set; }

/// <summary>
/// 坦克向左的图片
/// </summary>
public Bitmap BitmapLeft { get; set; }

/// <summary>
/// 坦克向右的图片
/// </summary>
public Bitmap BitmapRight { get; set; }

/// <summary>
/// 坦克移动速度
/// </summary>
public int Speed { get; set; }

/// <summary>
/// 我的坦克血量
/// </summary>
public int HP { get; set; }

/// <summary>
/// 坦克移动
/// </summary>
public void Move() {
switch (Direction)
{
case Direction.Up:
Y -= Speed;
break;
case Direction.Down:
Y += Speed;
break;
case Direction.Left:
X -= Speed;
break;
case Direction.Right:
X += Speed;
break;
}
}

/// <summary>
/// 获取坦克图片
/// </summary>
/// <returns> Image 坦克图片 </returns>
public override Image GetImage()
{
Bitmap bitmap;

//根据坦克朝向获取坦克图片
switch (Direction)
{
case Direction.Up:
bitmap = BitmapUp;
break;
case Direction.Down:
bitmap = BitmapDown;
break;
case Direction.Left:
bitmap = BitmapLeft;
break;
case Direction.Right:
bitmap = BitmapRight;
break;
default:
bitmap = BitmapUp;
break;
}
return bitmap;
}

/// <summary>
/// 改变坦克方向
/// </summary>
public abstract void ChangeDirection(KeyEventArgs e);

/// <summary>
/// 获取自身的二维矩形
/// </summary>
/// <returns></returns>
public override Rectangle GetRectangle()
{
return new Rectangle(X, Y, GetImage().Width, GetImage().Height);
}

/// <summary>
/// 获取自身移动后的二维矩形
/// </summary>
/// <returns></returns>
public Rectangle GetMoveRectangle()
{
Rectangle rectangle = GetRectangle();
switch (Direction)
{
case Direction.Up:
rectangle.Y -= Speed;
break;
case Direction.Down:
rectangle.Y += Speed;
break;
case Direction.Left:
rectangle.X -= Speed;
break;
case Direction.Right:
rectangle.X += Speed;
break;
}
return rectangle;
}

/// <summary>
/// 添加了坦克图片和坦克移动速度参数
/// </summary>
public Tank(int x, int y, Graphics graphics, int speed,
Bitmap bitmapUp, Bitmap bitmapDown, Bitmap bitmapRight, Bitmap bitmapLeft) : base(x, y, graphics)
{
this.Speed = speed;
this.Direction = Direction.Up; //坦克默认朝上
this.BitmapUp = bitmapUp; //坦克向上的图片
this.BitmapDown = bitmapDown; //坦克向下的图片
this.BitmapRight = bitmapRight; //坦克向右的图片
this.BitmapLeft = bitmapLeft; //坦克向左的图片
this.HP = 3; //可以承受三次攻击

//去除图片黑色背景
BitmapUp.MakeTransparent(Color.Black);
BitmapDown.MakeTransparent(Color.Black);
BitmapRight.MakeTransparent(Color.Black);
BitmapLeft.MakeTransparent(Color.Black);
}
}

/// <summary>
/// 我的坦克类
/// </summary>
class MyTank : Tank
{
/// <summary>
/// 我的坦克初始化方法
/// </summary>
/// <param name="x"> 窗口 X 坐标 </param>
/// <param name="y"> 窗口 Y 坐标 </param>
/// <param name="graphics"> 画布 </param>
/// <param name="speed"> 坦克移动速度 </param>
/// <param name="bitmapUp"> 坦克朝上图片 </param>
/// <param name="bitmapDown"> 坦克朝下图片 </param>
/// <param name="bitmapRight"> 坦克朝右图片 </param>
/// <param name="bitmapLeft"> 坦克朝左图片 </param>
public MyTank(int x, int y, Graphics graphics, int speed,
Bitmap bitmapUp, Bitmap bitmapDown, Bitmap bitmapRight, Bitmap bitmapLeft)
: base(x, y, graphics, speed, bitmapUp, bitmapDown, bitmapRight, bitmapLeft)
{

}

/// <summary>
/// 改变坦克方向
/// </summary>
public override void ChangeDirection(KeyEventArgs e)
{
//根据按下的方向键调整坦克朝向
switch (e.KeyCode)
{
case Keys.Up:
Direction = Direction.Up;
break;
case Keys.Down:
Direction = Direction.Down;
break;
case Keys.Left:
Direction = Direction.Left;
break;
case Keys.Right:
Direction = Direction.Right;
break;
}
}
}

/// <summary>
/// 敌人坦克类
/// </summary>
class EnemyTank : Tank
{
/// <summary>
/// 初始化一个随机数
/// </summary>
private readonly Random Rdm = new Random();

/// <summary>
/// 子弹攻击速度每60帧攻击一次
/// </summary>
private int BulletSpeed = 60;

/// <summary>
/// 是否攻击
/// </summary
private bool _IsAttack = false;
public bool IsAttack {
get {
if (BulletSpeed == 0)
{
BulletSpeed = 60;
return true;
}
else {
BulletSpeed--;
}
return false;
}
set {
_IsAttack = value;
}
}

/// <summary>
/// 敌人的坦克初始化方法
/// </summary>
/// <param name="x"> 窗口 X 坐标 </param>
/// <param name="y"> 窗口 Y 坐标 </param>
/// <param name="graphics"> 画布 </param>
/// <param name="speed"> 坦克移动速度 </param>
/// <param name="bitmapUp"> 坦克朝上图片 </param>
/// <param name="bitmapDown"> 坦克朝下图片 </param>
/// <param name="bitmapRight"> 坦克朝右图片 </param>
/// <param name="bitmapLeft"> 坦克朝左图片 </param>
public EnemyTank(int x, int y, Graphics graphics, int speed,
Bitmap bitmapUp, Bitmap bitmapDown, Bitmap bitmapRight, Bitmap bitmapLeft)
: base(x, y, graphics, speed, bitmapUp, bitmapDown, bitmapRight, bitmapLeft)
{

}

/// <summary>
/// 改变坦克方向由电脑控制不需要按键参数
/// </summary>
/// <param name="Key"></param>
public override void ChangeDirection(KeyEventArgs e)
{
while (true)
{
Direction dir = (Direction) Rdm.Next(0, 4);
if (Direction == dir)
{
continue;
}
else
{
Direction = dir;
break;
}
}
}
}

子弹类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
class Bullet : GameObject
{

/// <summary>
/// 子弹朝向
/// </summary>
public Direction Direction { get; set; }

/// <summary>
/// 子弹图片
/// </summary>
public Bitmap Bitmap { get; set; }

/// <summary>
/// 子弹移动速度
/// </summary>
public int Speed { get; set; }

/// <summary>
/// 子弹是谁发射的
/// </summary>
public BulletTank BulletTank { get; set; }

public Bullet(int x, int y, Graphics graphics, int speed,
Direction direction, BulletTank bulletTank) : base (x, y, graphics)
{
this.Speed = speed;
this.Direction = direction;
this.BulletTank = bulletTank;
//根据坦克朝向处理子弹图片
switch (direction)
{
case Direction.Up:
Bitmap = Resources.BulletUp;
this.X = x + 1;
this.Y = y - Bitmap.Height / 2;
break;
case Direction.Down:
Bitmap = Resources.BulletDown;
this.X = x + 2;
this.Y = y + Bitmap.Height / 2;
break;
case Direction.Left:
Bitmap = Resources.BulletLeft;
this.X = x - Bitmap.Height / 2;
this.Y = y + Bitmap.Height / 2 - 8;
break;
case Direction.Right:
Bitmap = Resources.BulletRight;
this.X = x + Bitmap.Height / 2 + 5;
this.Y = y + Bitmap.Height / 2 - 10;
break;
}

}

public override Image GetImage()
{
//去除图片黑色背景
Bitmap.MakeTransparent(Color.Black);
return Bitmap;
}

public override Rectangle GetRectangle()
{
return new Rectangle(X + 8, Y + 8, 3, 3);
}

/// <summary>
/// 子弹移动
/// </summary>
public void Move()
{
switch (Direction)
{
case Direction.Up:
Y -= Speed;
break;
case Direction.Down:
Y += Speed;
break;
case Direction.Left:
X -= Speed;
break;
case Direction.Right:
X += Speed;
break;
}
}
}

Boss类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Boss : GameObject
{
/// <summary>
/// 资源图片
/// </summary>
public Image Img { get; set; }

/// <summary>
/// 自身的二维矩形
/// </summary>
public Rectangle Rectangle { get; set; }

/// <summary>
/// 有参构造方法
/// </summary>
/// <param name="x"> 窗口 X 坐标 </param>
/// <param name="y"> 窗口 Y 坐标 </param>
/// <param name="graphics"> 画布 </param>
/// <param name="img"> 图片资源 </param>
public Boss(int x, int y, Graphics graphics, Image img) : base(x, y, graphics)
{
this.Img = img;
this.Rectangle = new Rectangle(x, y, img.Width, img.Height);
}

public override Image GetImage()
{
return Img;
}

public override Rectangle GetRectangle()
{
return Rectangle;
}
}

墙实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/// <summary>
///
/// </summary>
class Wall : GameObject
{
/// <summary>
/// 资源图片
/// </summary>
public Image Img { get; set; }

/// <summary>
/// 自身的二维矩形
/// </summary>
public Rectangle Rectangle { get; set; }

/// <summary>
/// 有参构造方法
/// </summary>
/// <param name="x"> 窗口 X 坐标 </param>
/// <param name="y"> 窗口 Y 坐标 </param>
/// <param name="graphics"> 画布 </param>
/// <param name="img"> 图片资源 </param>
public Wall(int x, int y, Graphics graphics, Image img) : base(x, y, graphics)
{
this.Img = img;
this.Rectangle = new Rectangle(x, y, img.Width, img.Height);
}

public override Image GetImage()
{
return Img;
}

public override Rectangle GetRectangle()
{
return Rectangle;
}
}

爆炸效果类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class Explosion : GameObject
{
/// <summary>
/// 爆炸效果图片
/// </summary>
private Bitmap[] bmpArray = new Bitmap[] {
Resources.EXP1,
Resources.EXP2,
Resources.EXP3,
Resources.EXP4,
Resources.EXP5
};

/// <summary>
/// 图片播放计数器
/// </summary>
private int PlayIndex = -1;

/// <summary>
/// 是否播放完毕
/// </summary>
public bool IsPlayFinished { get; private set; }

public Explosion(int x, int y, Graphics graphics) : base(x, y, graphics)
{
this.X = x - bmpArray[0].Width / 2;
this.Y = y - bmpArray[0].Height / 2;
this.IsPlayFinished = false;
//去除黑色背景
foreach (Bitmap bmp in bmpArray)
{
bmp.MakeTransparent(Color.Black);
}
}

public override Image GetImage()
{
PlayIndex = PlayIndex + 1;
if (PlayIndex < 5) {
return bmpArray[PlayIndex];
}
IsPlayFinished = true;
return bmpArray[0];
}

//不需要
public override Rectangle GetRectangle()
{
return new Rectangle(0, 0, 0, 0);
}
}

管理类

墙的管理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/// <summary>
/// 墙的管理类
/// </summary>
class WallManager
{
/// <summary>
/// 普通墙的集合
/// </summary>
public static List<Wall> OrdinaryWallList = new List<Wall>();

/// <summary>
/// 钢铁墙的集合
/// </summary>
public static List<Wall> SteelWallList = new List<Wall>();

/// <summary>
/// 所有墙的集合
/// </summary>
public static List<Wall> WallList = new List<Wall>();

/// <summary>
/// 初始化
/// </summary>
/// <param name="BitmapGraphics"></param>
public static void Init(Graphics BitmapGraphics)
{
OrdinaryWallList = new List<Wall>();
SteelWallList = new List<Wall>();
WallList = new List<Wall>();

//初始化墙
InitWall(BitmapGraphics);
//把墙绘制到画布上
DrawWall();
}

/// <summary>
/// 把墙绘制到画布上
/// </summary>
public static void DrawWall()
{
WallList.ForEach(item =>
{
item.DrawSelf();
});
}

/// <summary>
/// 初始化墙
/// </summary>
/// <param name="BitmapGraphics"></param>
private static void InitWall(Graphics BitmapGraphics)
{
OrdinaryWallList.AddRange(CreateWall(1, 1, 5, Resources.wall, BitmapGraphics));
OrdinaryWallList.AddRange(CreateWall(3, 1, 5, Resources.wall, BitmapGraphics));
OrdinaryWallList.AddRange(CreateWall(5, 1, 4, Resources.wall, BitmapGraphics));
OrdinaryWallList.AddRange(CreateWall(7, 1, 3, Resources.wall, BitmapGraphics));
OrdinaryWallList.AddRange(CreateWall(9, 1, 4, Resources.wall, BitmapGraphics));
OrdinaryWallList.AddRange(CreateWall(11, 1, 5, Resources.wall, BitmapGraphics));
OrdinaryWallList.AddRange(CreateWall(13, 1, 5, Resources.wall, BitmapGraphics));

SteelWallList.AddRange(CreateWall(7, 5, 1, Resources.steel, BitmapGraphics));

OrdinaryWallList.AddRange(CreateWall(2, 7, 1, Resources.wall, BitmapGraphics));
OrdinaryWallList.AddRange(CreateWall(3, 7, 1, Resources.wall, BitmapGraphics));
OrdinaryWallList.AddRange(CreateWall(4, 7, 1, Resources.wall, BitmapGraphics));
OrdinaryWallList.AddRange(CreateWall(6, 7, 1, Resources.wall, BitmapGraphics));
OrdinaryWallList.AddRange(CreateWall(7, 7, 1, Resources.wall, BitmapGraphics));
OrdinaryWallList.AddRange(CreateWall(8, 7, 1, Resources.wall, BitmapGraphics));
OrdinaryWallList.AddRange(CreateWall(10, 7, 1, Resources.wall, BitmapGraphics));
OrdinaryWallList.AddRange(CreateWall(11, 7, 1, Resources.wall, BitmapGraphics));
OrdinaryWallList.AddRange(CreateWall(12, 7, 1, Resources.wall, BitmapGraphics));

SteelWallList.AddRange(CreateWall(0, 7, 1, Resources.steel, BitmapGraphics));
SteelWallList.AddRange(CreateWall(14, 7, 1, Resources.steel, BitmapGraphics));

OrdinaryWallList.AddRange(CreateWall(1, 9, 5, Resources.wall, BitmapGraphics));
OrdinaryWallList.AddRange(CreateWall(3, 9, 5, Resources.wall, BitmapGraphics));
OrdinaryWallList.AddRange(CreateWall(5, 9, 3, Resources.wall, BitmapGraphics));

OrdinaryWallList.AddRange(CreateWall(6, 10, 1, Resources.wall, BitmapGraphics));
OrdinaryWallList.AddRange(CreateWall(7, 9, 3, Resources.wall, BitmapGraphics));
OrdinaryWallList.AddRange(CreateWall(8, 10, 1, Resources.wall, BitmapGraphics));

OrdinaryWallList.AddRange(CreateWall(9, 9, 3, Resources.wall, BitmapGraphics));
OrdinaryWallList.AddRange(CreateWall(11, 9, 5, Resources.wall, BitmapGraphics));
OrdinaryWallList.AddRange(CreateWall(13, 9, 5, Resources.wall, BitmapGraphics));

OrdinaryWallList.AddRange(CreateWall(6, 13, 2, Resources.wall, BitmapGraphics));
OrdinaryWallList.AddRange(CreateWall(7, 13, 1, Resources.wall, BitmapGraphics));
OrdinaryWallList.AddRange(CreateWall(8, 13, 2, Resources.wall, BitmapGraphics));

OrdinaryWallList.ForEach(item =>
{
WallList.Add(item);
});

SteelWallList.ForEach(item =>
{
WallList.Add(item);
});
}

/// <summary>
/// 创建墙
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="count"></param>
/// <param name="image"></param>
/// <param name="BitmapGraphics"></param>
/// <returns></returns>
private static List<Wall> CreateWall(int x, int y, int count, Image image, Graphics bitmapGraphics)
{
//墙的大小是一个30*30的正方形
int xPosition = x * 30;
int yPosition = y * 30;
List<Wall> results = new List<Wall>();
for (int i = yPosition; i < yPosition + count * 30; i += 15)
{
Wall wall1 = new Wall(xPosition, i, bitmapGraphics, image);
Wall wall2 = new Wall(xPosition + 15, i, bitmapGraphics, image);
results.Add(wall1);
results.Add(wall2);
}
return results;
}
}

Boss管理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/// <summary>
/// Boss管理类
/// </summary>
class BossManager
{
/// <summary>
/// Boss
/// </summary>
public static Boss Boss = null;

/// <summary>
/// 游戏是否结束
/// </summary>
public static bool IsGameOver = false;

/// <summary>
/// 初始化
/// </summary>
/// <param name="BitmapGraphics"></param>
public static void Init(Graphics bitmapGraphics)
{
//初始化Boss
CreateBoss(7, 14, Resources.Boss, bitmapGraphics);
//把 Boss 绘制到画布上
DrawBoss();
}

/// <summary>
/// 绘制boss
/// </summary>
public static void DrawBoss()
{
Boss.DrawSelf();
}

/// <summary>
/// 游戏结束
/// </summary>
public static void GameOver(Graphics bitmapGraphics)
{
Bitmap bmp = Resources.GameOver;
int x = 450 / 2 - Resources.GameOver.Width / 2;
int y = 450 / 2 - Resources.GameOver.Height / 2;
bitmapGraphics.DrawImage(bmp, x, y);
}

//创建Boss
private static void CreateBoss(int x, int y, Image image, Graphics bitmapGraphics)
{
int xPosition = x * 30;
int yPosition = y * 30;
Boss = new Boss(xPosition, yPosition, bitmapGraphics, image);
}
}

坦克管理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/// <summary>
/// 坦克管理类
/// </summary>
class TankManager
{

/// <summary>
/// 我的坦克
/// </summary>
public static MyTank MyTank { get; set; }

/// <summary>
/// 敌人的坦克集合
/// </summary>
public static List<EnemyTank> EnemyTankList = new List<EnemyTank>();

public static int EnemyTankCount = 10;

/// <summary>
/// 初始化
/// </summary>
/// <param name="BitmapGraphics"></param>
public static void Init(Graphics bitmapGraphics)
{
//初始化我的坦克
CreateMyTank(5, 14, bitmapGraphics);
//初始化敌人的坦克
EnemyTankList = new List<EnemyTank>();
//绘制我的坦克
DrawMyTank();
//刷新敌人的坦克
Task.Run(() => CreateEnemyTank(bitmapGraphics));
}

/// <summary>
/// 绘制我的坦克
/// </summary>
public static void DrawMyTank()
{
MyTank.DrawSelf();
}

/// <summary>
/// 绘制敌人的坦克
/// 敌人坦克需要一直移动
/// </summary>
public static void DrawEnemyTank()
{
BulletManager.CreateEnemyTankBullet();
lock (EnemyTankList) {
EnemyTankList.ForEach(enemyTank =>
{
EnemyTankMove(enemyTank);
enemyTank.DrawSelf();
});
}
}

/// <summary>
/// 键盘按下事件
/// </summary>
/// <param name="e"></param>
public static void KeyDown(KeyEventArgs e)
{
//改变坦克方向
MyTank.ChangeDirection(e);
//判断坦克移动后是否触碰边界
bool boundary = PZJCManager.TankBoundary(MyTank);
if (boundary)
{
//获取坦克移动后的二维矩形
Rectangle rectangle = MyTank.GetMoveRectangle();
//判断是否跟墙有碰撞
Wall wall = PZJCManager.CollisionDetectionWall(rectangle);
if (wall == null)
{
MyTank.Move();
}
}
}

/// <summary>
/// 复活我的坦克
/// </summary>
public static void RebirthMyTank()
{
CreateMyTank(5, 14, MyTank.Graphics);
}

//创建我的坦克
private static void CreateMyTank(int x, int y, Graphics bitmapGraphics)
{
int xPosition = x * 30;
int yPosition = y * 30;
int speed = 2;
MyTank = new MyTank(xPosition, yPosition, bitmapGraphics, speed,
Resources.MyTankUp, Resources.MyTankDown,
Resources.MyTankRight, Resources.MyTankLeft);
}

//初始化出生地点
private static Point[] points = new Point[] {
new Point(0, 0),
new Point(7 * 30, 0),
new Point(14 * 30, 0)
};

//初始化一个随机数类
private static readonly Random random = new Random();

//创建敌人的坦克
private static void CreateEnemyTank(Graphics bitmapGraphics)
{
while (true) {
if (EnemyTankList.Count == EnemyTankCount)
{
continue;
}

if (EnemyTankList.Count > 3) {
Thread.Sleep(1000*10);
SoundManager.PlayAdd();
}

//获取一个出生地点
Point point = points[random.Next(0, 3)];

//随机初始化一个敌人
EnemyTank enemyTank = null;
switch (random.Next(1, 5))
{
case 1:
enemyTank = new EnemyTank(point.X, point.Y, bitmapGraphics, 2,
Resources.GrayUp, Resources.GrayDown, Resources.GrayRight, Resources.GrayLeft);
break;
case 2:
enemyTank = new EnemyTank(point.X, point.Y, bitmapGraphics, 2,
Resources.GreenUp, Resources.GreenDown, Resources.GreenRight, Resources.GreenLeft);
break;
case 3:
enemyTank = new EnemyTank(point.X, point.Y, bitmapGraphics, 4,
Resources.QuickUp, Resources.QuickDown, Resources.QuickRight, Resources.QuickLeft);
break;
case 4:
enemyTank = new EnemyTank(point.X, point.Y, bitmapGraphics, 1,
Resources.SlowUp, Resources.SlowDown, Resources.SlowRight, Resources.SlowLeft);
break;
}
lock (EnemyTankList)
{
EnemyTankList.Add(enemyTank);
}
}
}

//敌人的坦克移动
private static void EnemyTankMove(EnemyTank tank)
{
//百分之五十的几率向下转向一次
if (random.Next(0, 50) == 0)
{
tank.Direction = Direction.Down;
}
while (true)
{
//检测移动后是否超出边界
bool boundary = PZJCManager.TankBoundary(tank);
//检测是否碰撞到物体
if (boundary)
{
//获取坦克移动后的二维矩形
Rectangle rectangle = tank.GetMoveRectangle();
//判断是否跟墙有碰撞
Wall wall = PZJCManager.CollisionDetectionWall(rectangle);
if (wall == null)
{
tank.Move();
break;
}
else
{
//随机获取一个移动方向
tank.ChangeDirection(null);
}
}
else
{
//随机获取一个移动方向
tank.ChangeDirection(null);
}
}
}
}

子弹管理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/// <summary>
/// 子弹管理类
/// </summary>
class BulletManager
{

/// <summary>
/// 子弹集合
/// </summary>
public static List<Bullet> BulletList = new List<Bullet>();

/// <summary>
/// 待删除子弹集合
/// </summary>
public static List<Bullet> RemoveBulletList = new List<Bullet>();

/// <summary>
/// 初始化子弹
/// </summary>
public static void Init()
{
BulletList = new List<Bullet>();
RemoveBulletList = new List<Bullet>();
}

/// <summary>
/// 绘制子弹
/// </summary>
public static void DrawBullet()
{
lock (BulletList)
{
//攻击
Attack();
//绘图
BulletList.ForEach(item =>
{
item.DrawSelf();
});
}
}

/// <summary>
/// 子弹攻击
/// </summary>
public static void Attack()
{
lock (BulletList)
{
foreach (var item in BulletList)
{
//子弹的二维矩形
Rectangle rectangle = item.GetRectangle();

//攻击普通墙
Wall ordinaryWall = PZJCManager.BulletOrdinaryWall(rectangle);
if (ordinaryWall != null)
{
ExplosionManager.CreateExplosion(item);//爆炸效果
if (item.BulletTank == BulletTank.MyTank) {
SoundManager.PlayBlast();//爆炸音效
}
RemoveBulletList.Add(item);//移除子弹
WallManager.WallList.Remove(ordinaryWall);//移除墙
WallManager.OrdinaryWallList.Remove(ordinaryWall);//移除墙
continue;
}

//攻击钢铁墙
Wall steelWall = PZJCManager.BulletSteelWall(rectangle);
if (steelWall != null)
{
ExplosionManager.CreateExplosion(item);//爆炸效果
if (item.BulletTank == BulletTank.MyTank)
{
SoundManager.PlayBlast();//爆炸音效
}
RemoveBulletList.Add(item);//移除子弹
continue;
}

//我的坦克攻击敌人
if (item.BulletTank == BulletTank.MyTank)
{
EnemyTank enemyTank = PZJCManager.BulletEnemyTank(rectangle);
if (enemyTank != null)
{
ExplosionManager.CreateExplosion(item);//爆炸效果
SoundManager.PlayBlast();//爆炸音效
RemoveBulletList.Add(item);//移除子弹
TankManager.EnemyTankList.Remove(enemyTank);//移除敌人的坦克
continue;
}
}
//敌人的坦克攻击我的坦克
else if (item.BulletTank == BulletTank.EnemyTank)
{
bool myTankEnd = PZJCManager.BulletMyTank(rectangle);
if (myTankEnd)
{
RemoveBulletList.Add(item);//移除子弹
TankManager.MyTank.HP -= 1;//每次扣除 1 滴血
SoundManager.PlayHit();
if (TankManager.MyTank.HP == 0)
{
TankManager.RebirthMyTank();//没血了复活我的坦克
ExplosionManager.CreateExplosion(item);//爆炸效果
SoundManager.PlayBlast();//爆炸音效
}
continue;
}
}

//攻击到BOSS
bool BOSS = PZJCManager.BulletBOSS(rectangle);
if (BOSS)
{
ExplosionManager.CreateExplosion(item);//爆炸效果
SoundManager.PlayBlast();//爆炸音效
RemoveBulletList.Add(item);//移除子弹
BossManager.IsGameOver = true;//游戏结束
continue;
}

//什么都没有攻击到就继续往前移动超出边界销毁
bool boundary = PZJCManager.BulletBoundary(item);
if (boundary)
{
item.Move();
}
else
{
lock (RemoveBulletList)
{
RemoveBulletList.Add(item);
}
}
}
RemoveBullet();//移除子弹
}
}

/// <summary>
/// 创建我的坦克发射的子弹
/// </summary>
/// <param name="bitmapGraphics"></param>
public static void CreateMyTankBullet()
{
CreateBullet(TankManager.MyTank.X, TankManager.MyTank.Y, TankManager.MyTank.Graphics,
TankManager.MyTank.Direction, BulletTank.MyTank);
SoundManager.PlayFire();
}

/// <summary>
/// 创建敌人坦克发射的子弹
/// </summary>
public static void CreateEnemyTankBullet()
{
lock (TankManager.EnemyTankList) {
foreach (var item in TankManager.EnemyTankList)
{
if (item.IsAttack)
{
CreateBullet(item.X, item.Y, item.Graphics, item.Direction, BulletTank.EnemyTank);
}
}
}
}

/// <summary>
/// 移除子弹
/// </summary>
/// <param name="bullet"></param>
public static void RemoveBullet()
{
lock (BulletList)
{
foreach (Bullet bullet in RemoveBulletList)
{
BulletList.Remove(bullet);
}

lock (RemoveBulletList)
{
RemoveBulletList.Clear();
}
}
}

/// <summary>
/// 创建一个子弹
/// </summary>
private static void CreateBullet(int x, int y, Graphics bitmapGraphics, Direction direction, BulletTank bulletTank)
{
int speed = 8;
Bullet bullet = new Bullet(x, y, bitmapGraphics, speed, direction, bulletTank);
lock (BulletList)
{
BulletList.Add(bullet);
}
}

}

声音管理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/// <summary>
/// 声音管理类
/// </summary>
class SoundManager
{
private static SoundPlayer startPlayer = new SoundPlayer();
private static SoundPlayer addPlayer = new SoundPlayer();
private static SoundPlayer blastPlayer = new SoundPlayer();
private static SoundPlayer firePlayer = new SoundPlayer();
private static SoundPlayer hitPlayer = new SoundPlayer();

/// <summary>
/// 声音初始化
/// </summary>
public static void Init()
{
startPlayer.Stream = Resources.start;
addPlayer.Stream = Resources.add;
blastPlayer.Stream = Resources.blast;
firePlayer.Stream = Resources.fire;
hitPlayer.Stream = Resources.hit;
}

/// <summary>
/// 开始音效
/// </summary>
public static void PlayStart()
{
startPlayer.Play();
}

/// <summary>
/// 生成敌人音效
/// </summary>
public static void PlayAdd()
{
addPlayer.Play();
}

/// <summary>
/// 爆炸的音效
/// </summary>
public static void PlayBlast()
{
blastPlayer.Play();
}

/// <summary>
///
/// </summary>
public static void PlayFire()
{
firePlayer.Play();
}

/// <summary>
/// 我的坦克被攻击到的音效
/// </summary>
public static void PlayHit()
{
hitPlayer.Play();
}
}

爆炸效果管理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/// <summary>
/// 爆炸效果管理类
/// </summary>
class ExplosionManager
{

/// <summary>
/// 爆照效果集合
/// </summary>
public static List<Explosion> ExplosionList = new List<Explosion>();

/// <summary>
/// 初始化
/// </summary>
public static void Init()
{
ExplosionList = new List<Explosion>();
}

/// <summary>
/// 绘制爆炸效果
/// </summary>
public static void DrawExplosion()
{
lock (ExplosionList)
{
//移除播放完毕的爆炸效果
ExplosionList.RemoveAll(item => item.IsPlayFinished);
if (ExplosionList.Count > 0)
{
foreach (var item in ExplosionList)
{
item.DrawSelf();
}
}
}
}

/// <summary>
/// 创建一个爆照效果
/// </summary>
public static void CreateExplosion(Bullet bullet)
{
int xExplosion = bullet.X + bullet.GetImage().Width / 2;
int yExplosion = bullet.Y + bullet.GetImage().Height / 2;
Explosion explosion = new Explosion(xExplosion, yExplosion, bullet.Graphics);
lock (ExplosionList)
{
ExplosionList.Add(explosion);
}
}
}

碰撞检测管理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/// <summary>
/// 碰撞检测管理类
/// </summary>
class PZJCManager
{
/// <summary>
/// 检测坦克是否超出边界
/// </summary>
/// <returns></returns>
/// <remarks>
/// false坦克超出边界不能移动
/// true 坦克没有超出边界可以移动
/// </remarks>
public static bool TankBoundary(Tank tank)
{
Image image = tank.GetImage();
if (tank.Direction == Direction.Up)
{
if (tank.Y - tank.Speed < 0)
{
return false;
}
}
else if (tank.Direction == Direction.Down)
{
if (tank.Y + tank.Speed + image.Height > 450)
{
return false;
}
}
else if (tank.Direction == Direction.Left)
{
if (tank.X - tank.Speed < 0)
{
return false;
}
}
else if (tank.Direction == Direction.Right)
{
if (tank.X + tank.Speed + image.Width > 450)
{
return false;
}
}
return true;
}

/// <summary>
/// 检测子弹是否超出边界
/// </summary>
/// <param name="bullet"></param>
/// <returns></returns>
/// <remarks>
/// false子弹超出边界不能移动
/// true 子弹没有超出边界可以移动
/// </remarks>
public static bool BulletBoundary(Bullet bullet)
{
Image image = bullet.GetImage();
if (bullet.Direction == Direction.Up)
{
if (bullet.Y + image.Height / 2 + 3 < 0)
{
return false;
}
}
else if (bullet.Direction == Direction.Down)
{
if (bullet.Y + image.Height / 2 - 3 > 450)
{
return false;
}
}
else if (bullet.Direction == Direction.Left)
{
if (bullet.X + image.Width / 2 - 3 < 0)
{
return false;
}
}
else if (bullet.Direction == Direction.Right)
{
if (bullet.X + image.Width / 2 + 3 > 450)
{
return false;
}
}
return true;
}

/// <summary>
/// 子弹碰撞到普通墙
/// </summary>
public static Wall BulletOrdinaryWall(Rectangle rectangle)
{
foreach (Wall wall in WallManager.OrdinaryWallList)
{
if (wall.Rectangle.IntersectsWith(rectangle))
{
return wall;
}
}
return null;
}

/// <summary>
/// 子弹碰撞到钢墙
/// </summary>
public static Wall BulletSteelWall(Rectangle rectangle)
{
foreach (Wall wall in WallManager.SteelWallList)
{
if (wall.Rectangle.IntersectsWith(rectangle))
{
return wall;
}
}
return null;
}

/// <summary>
/// 子弹是否攻击到坦克
/// </summary>
public static EnemyTank BulletEnemyTank(Rectangle rectangle)
{
lock (TankManager.EnemyTankList) {
foreach (EnemyTank enemyTank in TankManager.EnemyTankList)
{
if (enemyTank.GetRectangle().IntersectsWith(rectangle))
{
return enemyTank;
}
}
}
return null;
}

/// <summary>
/// 子弹是否攻击到我的坦克
/// </summary>
public static bool BulletMyTank(Rectangle rectangle)
{
if (TankManager.MyTank.GetRectangle().IntersectsWith(rectangle))
{
return true;
}
return false;
}

/// <summary>
/// 子弹是否攻击到BOSS
/// </summary>
public static bool BulletBOSS(Rectangle rectangle)
{
if (BossManager.Boss.GetRectangle().IntersectsWith(rectangle))
{
return true;
}
return false;
}

/// <summary>
/// 检测跟墙是否发生碰撞
/// </summary>
/// <param name="rectangle"></param>
/// <returns></returns>
public static Wall CollisionDetectionWall(Rectangle rectangle)
{
foreach (Wall wall in WallManager.WallList)
{
if (wall.Rectangle.IntersectsWith(rectangle))
{
return wall;
}
}
return null;
}
}

学习资源