C# 概述 C#( 发音为 “C-Sharp”) 是一种现代的、 面向对象的、 类型安全的编程语言, 由微软公司开发, 并于2000年首次发布。 C# 是在.NET框架上运行的 , 后来随着 .NET Core 和 .NET 5+ 的推出, 它也支持跨平台开发。 C# 结合了 C 和 C++ 的强大功能以及 Java 的一些特性, 旨在提高程序员的生产力。
⭐⭐特点:
面向对象: C# 支持封装、 继承和多态等面向对象的编程特性。
类型安全: C# 防止类型不匹配错误, 确保程序中的数据类型在编译时被正确检查。
垃圾回收: C# 自动管理内存, 通过垃圾回收机制自动释放不再使用的对象, 简化了内存管理。
简单性与可读性: C# 的语法设计简洁, 易于学习和阅读。
强大的类库: .NET Framework 提供了丰富的类库, 包括文件处理、 数据库访问、 网络通信等功能。
多线程: C# 支持多线程编程, 可以编写高效并行的代码。
LINQ: C# 引入了 LINQ( Language 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, CLR) : CLR是.NET框架的执行引擎, 负责在应用程序运行时提供内存管理、 垃圾回收、 线程管理和异常处理等服务。 CLR还提供了类型安全检查和代码验证, 以确保代码的稳定性和安全性。
框架类库( Framework Class Library, FCL) : 它是.NET Framework中的一系列类库的总称, 包含了大量预定义的类和接口, 这些类和接口提供了各种各样的功能, 从基本的数据类型操作到复杂的网络通信和数据库交互。
基础类库( Base Class Library, BCL) : BCL是.NET框架中的一系列预编译的类库, 它们为开发者提供了丰富的API, 涵盖了文件I/O、 网络通信、 数据处理、 图形绘制等众多领域, 极大地简化了开发过程。
语言互操作性: .NET支持多种编程语言, 包括C#、 Visual Basic、 F#等, 并允许这些语言之间的互操作, 即可以在同一项目中混合使用不同语言编写的代码。
跨平台支持: 虽然最初的.NET框架主要针对Windows平台, 但后来推出的.NET Core和.NET 5/6/7等版本已经实现了跨平台能力, 可以在Linux、 macOS等操作系统上运行。
开源和社区驱动: .NET Core和后续版本是开源的, 这吸引了大量的开发者参与其中, 形成了活跃的社区生态, 不断推动着.NET的发展和创新。
工具与IDE: 微软提供了Visual Studio作为.NET的主要集成开发环境( IDE) , 它拥有强大的编辑、 调试、 构建等功能。 此外, 还有许多其他IDE和文本编辑器也支持.NET开发, 如Visual Studio Code、 Rider等。
高性能和现代化: .NET不断优化性能, 支持现代开发模式, 如异步编程、 依赖注入、 微服务架构等, 同时提供了对云原生、 容器化等技术的支持。
ASP.NET: ASP.NET是.NET框架中的Web应用开发框架, 提供了MVC、 Web API等多种Web开发模型, 以及信号R( SignalR) 等实时通信功能。
💗💗.NET自2002年首次发布以来, 经历了多个版本的迭代, 从.NET Framework到.NET Core, 再到统一的.NET 5/6/7, 持续地改进和扩展其功能, 成为了现代软件开发的重要平台之一。 .NET框架的名词缩写
公共中间语言: CIL( Common Intermediate Language) 、 IL( Intermediate Language) 、 MSIL( Microsoft Intermediate Language)
公共语言运行库: CLR( Common Language Runtime)
基类库: BCL( Base Class Library)
垃圾收集: GC( Garbage Collection)
实时编译器: JIT( Just-In-Time)
公共语言基础结构: CLI( Common Language Infrastructure)
公共类型系统: CTS( Common Type System)
公共语言规范: CLS( Common Language Specification)
.NET框架的组成 graph TB;
A[IDE: 集成开发环境] --> B[MSIL: 编译的中间语言];
A --> C[FCL: 类库]
B --> D[CLR: 转换对应平台可执行的代码];
D --> C
D --> F[应用程序执行]
G[
1. 开发者使用IDE( Visual Studio) 编写源代码( 使用C、 C++、 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) 。 创建项目
点击创建新项目
选择C#控制台应用 → 下一步
项目名称: HelloWorld
位置: D:\VisualStudio Files\Project\
解决方案名称: Day01
下一步 -> 创建
💗💗解决方案是一个或多个项目的集合, 它们通常代表了一个较大的软件系统或一组相关联的应用程序。 💗💗项目是构成解决方案的基本单元, 代表了软件开发的一个具体目标, 比如一个类库、 一个控制台应用程序、 一个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 : 命名空间的名称{} 花括号: 作用域、 有效范围 class 类, Program : 类的名称Main 程序入口函数( 程序从这个函数开始运行)
注意事项
程序语句要用英文书写( 注释、 字符串随意)
字符严格区分大小写
代码注释 单行注释 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 ) { } } }
变量&常量 变量 ⭐⭐变量: 值可以改变, 初始化的时候不需要赋值。
变量是用于存储数据的命名存储位置
变量具有类型、 名称和值
类型决定了变量可以存储的数据种类
名称则用来标识这个特定的变量
值是变量所保存的实际信息
常量 ⭐⭐常量: 值不可以改变, 初始化的时候需要赋值。
命名规范
变量名必须以字母(大小写均可)或下划线开头
变量名只能包含字母(大小写), 数字, 下划线
变量名不能使用关键字
变量名不能重复
变量名尽量有意义
💗💗常用命名规范
变量可以由多个单词组成
帕斯卡命名法: 变量可以由多个单词组成, 每个单词首字母大写, 通常用于类型名和方法名, 例: StudentName
驼峰命名法: , 变量第一个单词首字母小写, 其他单词首字母大写, 通常用于变量名, 例: studentName
匈牙利命名法: 首字母使用小写数据类型标识符, 其他使用帕斯卡命名, 例: sStudentName
全大写命名法: 单词全部大写, 通常用于常量和单词缩写, 例: SN
下划线连接命名法: 变量可以由多个单词组成, 单词之间用下划线连接, 通常用于常量, 例: STUDENT_NAME
数值类型 整数类型 💗💗无符号意味着它只表示正数( 大于0的数) 💗💗有符号意味着它既可以表示正数也可以表示负数和0
byte: 8位无符号整数
sbyte: 8位有符号整数
short 或 Int16: 16位有符号整数
ushort 或 UInt16: 16位无符号整数
int 或 Int32: 32位有符号整数
uint 或 UInt32: 32位无符号整数
long 或 Int64: 64位有符号整数
ulong 或 UInt64: 64位无符号整数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 byte byteVar = 50 ;sbyte sbyteVar = -50 ;short shortVar = -32767 ;ushort ushortVar = 65535 ;int intVar = -2147483647 ;uint uintVar = 4294967295u ; long longVar = -9223372036854775807L ; ulong ulongVar = 18446744073709551615U L;
浮点数类型
float: 32位单精度浮点数
double: 64位双精度浮点数
decimal: 128位十进制浮点数, 用于货币计算
1 2 3 4 5 6 7 8 float floatVar = 3.14159f ; double doubleVar = 3.14159 ; decimal decimalVar = 123.456 m;
非数值类型
字符类型: char
字符串类型: string ( 多个字符串可以用 + 号连接)
布尔类型: bool ( 布尔值, 可以是true或false)
1 2 3 4 5 6 7 8 9 10 11 char ch = 'A' ; string str = "Hello, world" ; string fullName = str + " " + "!" ;bool isTrue = true ;
类型转换 隐式转换 隐式转换发生在当一个较小的类型可以安全地转换为较大的类型时, 例如从 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 ;int intValue = (int ) doubleValue;
使用 Convert 类 💗💗 System 命名空间下的 Convert 类提供了多种静态方法来转换不同类型的数据。 1 2 3 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);
使用 as 关键字和类型转换运算符 1 2 3 4 5 object obj = new int [] { 1 , 2 , 3 };int [] arr = obj as int []; object obj2 = "Hello" ;string str = (string )obj2;
使用 checked 和 unchecked 关键字 1 2 3 4 5 6 7 8 9 10 11 checked { int a = int .MaxValue; int b = a + 1 ; } unchecked { int a = int .MaxValue; int b = a + 1 ; }
运算符 数学运算符
运 算 符 示例表达式 结 果
+ 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 + 1, var2 递增 1
-- var1 = --var2; var1 的值是 var2 - 1, var2 递减 1
++ var1 = var2++; var1 的值是 var2, var2 递增 1
-- var1 = var2--; var1 的值是 var2, var2 递减 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 等于 var3, var1 的值就是 true, 否则为 false
!= var1 = var2 != var3; 如果 var2 不等于 var3, var1 的值就是 true, 否则为 false
< var1 = var2 < var3; 如果 var2 小于 var3, var1 的值就是 true, 否则为 false
> var1 = var2 > var3; 如果 var2 大于 var3, var1 的值就是 true, 否则为 false
<= var1 = var2 <= var3; 如果 var2 小于等于 var3, var1 的值就是 true, 否则为 false
>= var1 = var2 >= var3; 如果 var2 大于等于 var3, var1 的值就是 true, 否则为 false
逻辑运算符
运 算 符 示例表达式 结 果
&& var1 = var2 && var3; 如果 var2 和 var3 都是 true, var1 的值就是 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! " ; Console.WriteLine($"Length: {str.Length} " ); Console.WriteLine($"First character: {str[0 ]} " ); string concatenated = string .Concat(str, " " , str2.Trim()); Console.WriteLine($"Concatenated: {concatenated} " ); int compareResult = string .Compare(str, str2, true ); Console.WriteLine($"Comparison (ignore case): {compareResult} " ); int compareToResult = str.CompareTo(str2); Console.WriteLine($"CompareTo result: {compareToResult} " ); bool containsWorld = str.Contains("world" ); Console.WriteLine($"Contains 'world': {containsWorld} " ); string copy = str.Copy(); Console.WriteLine($"Copied: {copy} " ); bool equalsResult = str.Equals(str2, StringComparison.OrdinalIgnoreCase); Console.WriteLine($"Equals (ignore case): {equalsResult} " ); int index = str.IndexOf("world" ); Console.WriteLine($"Index of 'world': {index} " ); int lastIndex = str.LastIndexOf("o" ); Console.WriteLine($"Last index of 'o': {lastIndex} " ); string inserted = str.Insert(7 , "beautiful " ); Console.WriteLine($"Inserted: {inserted} " ); string paddedLeft = str.PadLeft(20 , '*' ); Console.WriteLine($"Padded Left: {paddedLeft} " ); string paddedRight = str.PadRight(20 , '*' ); Console.WriteLine($"Padded Right: {paddedRight} " ); string removed = str.Remove(7 , 7 ); Console.WriteLine($"Removed: {removed} " ); string replaced = str.Replace("world" , "C#" ); Console.WriteLine($"Replaced: {replaced} " ); bool startsWithHello = str.StartsWith("Hello" ); Console.WriteLine($"StartsWith 'Hello': {startsWithHello} " ); bool endsWithExclamation = str.EndsWith("!" ); Console.WriteLine($"EndsWith '!': {endsWithExclamation} " ); string substring = str.Substring(7 , 5 ); Console.WriteLine($"Substring: {substring} " ); string lowercase = str.ToLower(); Console.WriteLine($"Lowercase: {lowercase} " ); string uppercase = str.ToUpper(); Console.WriteLine($"Uppercase: {uppercase} " ); string trimmed = str2.Trim(); Console.WriteLine($"Trimmed: '{trimmed} '" ); string trimmedStart = str2.TrimStart(); Console.WriteLine($"Trimmed Start: '{trimmedStart} '" ); string trimmedEnd = str2.TrimEnd(); Console.WriteLine($"Trimmed End: '{trimmedEnd} '" ); string [] splitArray = str.Split(',' ); foreach (var item in splitArray) { Console.WriteLine($"Split item: {item} " ); } string joined = string .Join("-" , splitArray); Console.WriteLine($"Joined: {joined} " ); string formatted = string .Format("Message: {0}, Time: {1}" , str, DateTime.Now); Console.WriteLine($"Formatted: {formatted} " ); string nullOrEmpty = string .IsNullOrEmpty(null ); Console.WriteLine($"IsNullOrEmpty (null): {nullOrEmpty} " ); bool isNullOrWhiteSpace = string .IsNullOrWhiteSpace(" " ); Console.WriteLine($"IsNullOrWhiteSpace (' '): {isNullOrWhiteSpace} " ); int hashCode = str.GetHashCode(); Console.WriteLine($"Hash code: {hashCode} " ); byte [] bytes = System.Text.Encoding.UTF8.GetBytes(str); Console.WriteLine($"Bytes: {BitConverter.ToString(bytes)} " ); char [] chars = str.ToCharArray(); Console.WriteLine($"Chars: {string .Join(", " , chars)} " ); 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 ) { } else if (var1 == 2 ) { } else if (var1 == 3 || var1 == 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 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 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 2 3 4 5 6 7 8 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 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 ][]; 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 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 public int Add (int x, int y ){ return 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); } } } 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 ) ;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 ) ;
⭐⭐使用示例 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 sayHello = () => Console.WriteLine("Hello!" ); sayHello(); 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 ) ;
⭐⭐使用示例 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<string > getGreeting = () => "Hello!" ; Console.WriteLine(getGreeting()); Func<int , int > square = x => x * x; Console.WriteLine(square(5 )); Func<int , int , bool > isEqual = (a, b) => a == b; Console.WriteLine(isEqual(10 , 10 )); } }
多播委托 多播委托是指一个委托实例可以关联多个方法的能力。 这意味着当你调用这个委托时, 所有关联的方法都会被依次调用。 这种特性在很多场合下都非常有用, 尤其是在事件处理中。
⭐⭐多播委托的工作原理 多播委托是通过 += 和 -= 运算符来实现方法的添加和移除的。
当一个方法被添加到委托时, 实际上是创建了一个包含多个方法调用的新委托。
因此, 当原始委托被调用时, 实际上是在执行一系列的方法。
⭐⭐注意事项
顺序: 当调用多播委托时, 方法按照它们添加到委托中的顺序依次调用。
异常处理: 如果其中一个方法抛出异常, 那么后续的方法将不再被调用。 为了避免这种情况, 你需要捕获异常。
性能影响: 多播委托可能会对性能产生一定影响, 尤其是在关联了很多方法的情况下。 这是因为每次调用委托都需要创建一个新的委托链。
⭐⭐使用示例 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;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); } } 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.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 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[] colors = (Color[]) Enum.GetValues(typeof (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; p2.X = 100 ; Console.WriteLine(p1.X);
常见用途
简单的数据组合
坐标点
颜色
用于需要高效内存访问和管理的场景等
面向对象 类( 对象) 类( 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; } 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(); myDog.NewMethod(); }
⭐⭐ 虚方法 虚方法( 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(); myCat.MakeSound(); }
⭐⭐ 隐藏方法 如果想要隐藏基类中的方法而不是重写它, 可以使用 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 { public new void MyMethod () { Console.WriteLine("DerivedClass.MyMethod" ); } public override void AnotherMethod () { base .AnotherMethod(); Console.WriteLine("DerivedClass.AnotherMethod" ); } } class Program { static void Main (string [] args ) { BaseClass obj = new DerivedClass(); obj.MyMethod(); obj.AnotherMethod(); ((DerivedClass)obj).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(); } }
⭐⭐ 构造函数 派生类的构造函数需要调用基类的构造函数。 可以通过 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); OnLogMessage(message); } partial void OnLogMessage (string message ) ; } public partial class Logger { 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} " ); } } public class Car : Vehicle { public Car (string make, string model ) : base (make, model ) { } public override void Drive () { Console.WriteLine($"The car is driving." ); } } 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 () ; } public class ElectricEngine : IEngine { public void Start () { Console.WriteLine("Electric engine started." ); } public void Stop () { Console.WriteLine("Electric engine stopped." ); } } 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); Console.WriteLine("Name: " + myType.Name); 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.ArgumentException、 System.NullReferenceException、 System.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 ; }
自定义异常 除了内置的异常类之外, 你还可以创建自己的异常类。 这通常是为了更好地描述特定类型的错误或者为了提供额外的信息。
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 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" )){ }
数据存储 栈 (Stack) 栈是一种后进先出 (LIFO, Last In First Out) 的数据结构, 在 C# 中用于存储局部变量和方法调用时的参数。 栈上的内存分配和释放速度非常快, 因为它是通过指针的移动来实现的( 例如, 压栈和弹栈操作) 。
⭐⭐ 特点
自动管理: 由编译器自动分配和释放。
速度快: 分配和释放内存的速度很快。
固定大小: 分配给栈的空间通常是固定的。
安全性: 栈上分配的变量不能被其他线程访问。
局部作用域: 栈中的变量只在其定义的作用域内可见。
⭐⭐ 示例 1 2 3 4 void Method (){ int x = 10 ; }
堆 (Heap) 堆是用于动态内存分配的区域, 主要用来存储对象实例和数组。 当一个对象或数组创建时, 其实际的数据会存储在堆上, 并通过引用类型( 如类、 接口等) 指向这些数据。
⭐⭐ 特点
手动管理: 程序员需要显式地分配和释放内存( 通过 new 关键字分配, 通过垃圾回收器自动回收) 。
可变大小: 可以动态调整大小。
速度较慢: 相对于栈来说, 分配和释放内存的速度较慢。
共享性: 堆上的对象可以被多个线程共享。
全局作用域: 堆上的对象可以在整个程序的生命周期内存在。
⭐⭐ 示例 1 2 3 4 5 6 7 8 9 10 class MyClass { public int Value; } void Method (){ MyClass obj = new MyClass(); 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(); } }
垃圾回收 垃圾回收 (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 (FileStream fs = new FileStream("path" , FileMode.Open)){ } 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()){ }
索引器 索引器( 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 ; 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 ; 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 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" ); string firstFruit = queue.Peek();string fruit = queue.Dequeue();Console.WriteLine(queue.Dequeue()); 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" ); string topFruit = stack.Peek();Console.WriteLine(topFruit); string fruit = stack.Pop();Console.WriteLine(fruit); 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 ; BitArray bits = new BitArray(new int [] { initialValue }); byte [] initialBytes = { 0x01 , 0x02 }; BitArray bits = new BitArray(initialBytes); bits.Set(2 , true ); bits.Set(2 , false ); bool bitValue = bits[2 ];bool bitValue2 = bits.Get(2 );BitArray result = (BitArray)bits.Clone(); result.And(otherBits); BitArray result = (BitArray)bits.Clone(); result.Or(otherBits); BitArray result = (BitArray)bits.Clone(); result.Xor(otherBits); 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 ); list.RemoveAll(item => item.StartsWith("A" )); list.Clear(); string firstPerson = names[0 ];List<string > range = list.GetRange(1 , 2 ); bool containsItem = list.Contains("item" );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());
⭐⭐Stack: 后进先出( LIFO) , 类型安全。 1 2 3 4 Stack<string > stack = new Stack<string >(); stack.Push("first" ); stack.Push("second" ); Console.WriteLine(stack.Pop());
⭐⭐ 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); }
⭐⭐ 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); }
⭐⭐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<string > originalList = new List<string >() { "apple" , "banana" , "cherry" }; 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 (){ } 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.Run(() => TaskFunction());
线程休眠
等待线程 1 2 3 4 5 thread.Join(); task.Wait();
取消线程 ⭐⭐ 示例一
⭐⭐ 示例二 使用 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 ); 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" ); 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" ); DirectoryInfo directoryInfo = Directory.CreateDirectory(@"C:\example" );
属性 Exists: 获取一个值 1 2 3 4 5 6 7 8 9 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 subDir = directoryInfo.CreateSubdirectory("subfolder" ); Console.WriteLine(subDir.FullName);
删除当前目录 1 2 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" ; 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 }; 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 = 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+" ; Match match = Regex.Match(text, pattern); if (match.Success) { Console.WriteLine($"Match without Multiline: {match.Value} " ); } else { Console.WriteLine("No match without Multiline." ); } match = Regex.Match(text, pattern, RegexOptions.Multiline); if (match.Success) { Console.WriteLine($"First match with Multiline: {match.Value} " ); } 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);
获取第一个匹配项 1 2 3 4 Match match = Regex.Match("123abc" , @"\d+" ); if (match.Success) { Console.WriteLine(match.Value); }
获取所有匹配项 1 2 3 4 MatchCollection matches = Regex.Matches("123 abc 456 def 789" , @"\d+" ); foreach (Match m in matches) { Console.WriteLine(m.Value); }
替换所有匹配的子串 1 2 string result = Regex.Replace("123abc" , @"\d+" , "#" );Console.WriteLine(result);
解码正则表达式中的转义序列 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+" ; 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 控件到窗体上。
设置 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.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 类用于控制文本的排列方式。
属性:
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" ); 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 类, 就是游戏窗口类, 所有的游戏操作方法, 都是在这里调用的。
⭐⭐ 添加游戏所需资源
窗口类 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 { private System.ComponentModel.IContainer components = null ; protected override void Dispose (bool disposing ) { if (disposing && (components != null )) { components.Dispose(); } base .Dispose(disposing); } #region Windows 窗体设计器生成的代码 private void InitializeComponent () { this .SuspendLayout(); 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 () { 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 public class GameFramework { const int SleepTime = 1000 / 60 ; private readonly Graphics Graphics; public Bitmap Bitmap; public Graphics BitmapGraphics; public GameFramework (Graphics graphics ) { this .Graphics = graphics; Bitmap = new Bitmap(450 , 450 ); BitmapGraphics = Graphics.FromImage(Bitmap); BitmapGraphics.Clear(Color.Black); } public void GameMainThread () { Init(); while (true ) { Update(); Thread.Sleep(SleepTime); } } private void Init () { SoundManager.Init(); WallManager.Init(BitmapGraphics); BossManager.Init(BitmapGraphics); TankManager.Init(BitmapGraphics); BulletManager.Init(); ExplosionManager.Init(); SoundManager.PlayStart(); } private void Update () { if (BossManager.IsGameOver) { BossManager.GameOver(BitmapGraphics); } else { RefreshBitmap(); } DrawWinForm(); } private void RefreshBitmap () { BitmapGraphics.Clear(Color.Black); WallManager.DrawWall(); BossManager.DrawBoss(); TankManager.DrawMyTank(); TankManager.DrawEnemyTank(); BulletManager.DrawBullet(); ExplosionManager.DrawExplosion(); } private void DrawWinForm () { Graphics.DrawImage(Bitmap, 0 , 0 ); } private bool IsSpaceKeyPressed = false ; 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); } else if (e.KeyCode == Keys.Space && !IsSpaceKeyPressed) { BulletManager.CreateMyTankBullet(); IsSpaceKeyPressed = true ; } } } 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 { public int X { get ; set ; } public int Y { get ; set ; } public Graphics Graphics { get ; set ; } public abstract Image GetImage () ; public abstract Rectangle GetRectangle () ; public virtual void DrawSelf () { lock (Graphics) { Graphics?.DrawImage(GetImage(), this .X, this .Y); } } 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 enum Direction{ Up, Down, Left, Right } enum BulletTank { MyTank, 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 abstract class Tank : GameObject { public Direction Direction { get ; set ; } public Bitmap BitmapUp { get ; set ; } public Bitmap BitmapDown { get ; set ; } public Bitmap BitmapLeft { get ; set ; } public Bitmap BitmapRight { get ; set ; } public int Speed { get ; set ; } public int HP { get ; set ; } 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 ; } } 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; } public abstract void ChangeDirection (KeyEventArgs e ) ; public override Rectangle GetRectangle () { return new Rectangle(X, Y, GetImage().Width, GetImage().Height); } 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; } 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); } } class MyTank : Tank { 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 ) { } 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 ; } } } class EnemyTank : Tank { private readonly Random Rdm = new Random(); private int BulletSpeed = 60 ; 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 ) { } 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 { public Direction Direction { get ; set ; } public Bitmap Bitmap { get ; set ; } public int Speed { get ; set ; } 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 ); } 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 { public Image Img { get ; set ; } public Rectangle Rectangle { get ; set ; } 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 class Wall : GameObject { public Image Img { get ; set ; } public Rectangle Rectangle { get ; set ; } 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 { private Bitmap[] bmpArray = new Bitmap[] { Resources.EXP1, Resources.EXP2, Resources.EXP3, Resources.EXP4, Resources.EXP5 }; private int PlayIndex = -1 ; 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 class WallManager { public static List<Wall> OrdinaryWallList = new List<Wall>(); public static List<Wall> SteelWallList = new List<Wall>(); public static List<Wall> WallList = new List<Wall>(); public static void Init (Graphics BitmapGraphics ) { OrdinaryWallList = new List<Wall>(); SteelWallList = new List<Wall>(); WallList = new List<Wall>(); InitWall(BitmapGraphics); DrawWall(); } public static void DrawWall () { WallList.ForEach(item => { item.DrawSelf(); }); } 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); }); } private static List<Wall> CreateWall (int x, int y, int count, Image image, Graphics bitmapGraphics ) { 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 class BossManager { public static Boss Boss = null ; public static bool IsGameOver = false ; public static void Init (Graphics bitmapGraphics ) { CreateBoss(7 , 14 , Resources.Boss, bitmapGraphics); DrawBoss(); } public static void DrawBoss () { Boss.DrawSelf(); } 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); } 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 class TankManager { public static MyTank MyTank { get ; set ; } public static List<EnemyTank> EnemyTankList = new List<EnemyTank>(); public static int EnemyTankCount = 10 ; public static void Init (Graphics bitmapGraphics ) { CreateMyTank(5 , 14 , bitmapGraphics); EnemyTankList = new List<EnemyTank>(); DrawMyTank(); Task.Run(() => CreateEnemyTank(bitmapGraphics)); } public static void DrawMyTank () { MyTank.DrawSelf(); } public static void DrawEnemyTank () { BulletManager.CreateEnemyTankBullet(); lock (EnemyTankList) { EnemyTankList.ForEach(enemyTank => { EnemyTankMove(enemyTank); enemyTank.DrawSelf(); }); } } 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(); } } } 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 class BulletManager { public static List<Bullet> BulletList = new List<Bullet>(); public static List<Bullet> RemoveBulletList = new List<Bullet>(); public static void Init () { BulletList = new List<Bullet>(); RemoveBulletList = new List<Bullet>(); } public static void DrawBullet () { lock (BulletList) { Attack(); BulletList.ForEach(item => { item.DrawSelf(); }); } } 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 ; SoundManager.PlayHit(); if (TankManager.MyTank.HP == 0 ) { TankManager.RebirthMyTank(); ExplosionManager.CreateExplosion(item); SoundManager.PlayBlast(); } continue ; } } 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(); } } public static void CreateMyTankBullet () { CreateBullet(TankManager.MyTank.X, TankManager.MyTank.Y, TankManager.MyTank.Graphics, TankManager.MyTank.Direction, BulletTank.MyTank); SoundManager.PlayFire(); } 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); } } } } public static void RemoveBullet () { lock (BulletList) { foreach (Bullet bullet in RemoveBulletList) { BulletList.Remove(bullet); } lock (RemoveBulletList) { RemoveBulletList.Clear(); } } } 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 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(); public static void Init () { startPlayer.Stream = Resources.start; addPlayer.Stream = Resources.add ; blastPlayer.Stream = Resources.blast; firePlayer.Stream = Resources.fire; hitPlayer.Stream = Resources.hit; } public static void PlayStart () { startPlayer.Play(); } public static void PlayAdd () { addPlayer.Play(); } public static void PlayBlast () { blastPlayer.Play(); } public static void PlayFire () { firePlayer.Play(); } 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 class ExplosionManager { public static List<Explosion> ExplosionList = new List<Explosion>(); public static void Init () { ExplosionList = new List<Explosion>(); } public static void DrawExplosion () { lock (ExplosionList) { ExplosionList.RemoveAll(item => item.IsPlayFinished); if (ExplosionList.Count > 0 ) { foreach (var item in ExplosionList) { item.DrawSelf(); } } } } 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 class PZJCManager { 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 ; } 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 ; } public static Wall BulletOrdinaryWall (Rectangle rectangle ) { foreach (Wall wall in WallManager.OrdinaryWallList) { if (wall.Rectangle.IntersectsWith(rectangle)) { return wall; } } return null ; } public static Wall BulletSteelWall (Rectangle rectangle ) { foreach (Wall wall in WallManager.SteelWallList) { if (wall.Rectangle.IntersectsWith(rectangle)) { return wall; } } return null ; } public static EnemyTank BulletEnemyTank (Rectangle rectangle ) { lock (TankManager.EnemyTankList) { foreach (EnemyTank enemyTank in TankManager.EnemyTankList) { if (enemyTank.GetRectangle().IntersectsWith(rectangle)) { return enemyTank; } } } return null ; } public static bool BulletMyTank (Rectangle rectangle ) { if (TankManager.MyTank.GetRectangle().IntersectsWith(rectangle)) { return true ; } return false ; } public static bool BulletBOSS (Rectangle rectangle ) { if (BossManager.Boss.GetRectangle().IntersectsWith(rectangle)) { return true ; } return false ; } public static Wall CollisionDetectionWall (Rectangle rectangle ) { foreach (Wall wall in WallManager.WallList) { if (wall.Rectangle.IntersectsWith(rectangle)) { return wall; } } return null ; } }
学习资源