1. 引言

C#(读作”C Sharp”)是一种由微软开发的现代、类型安全的面向对象编程语言。它是.NET框架的一部分,设计目标是结合Visual Basic的易用性和C++的强大功能。C#广泛用于开发桌面应用程序、Web应用、游戏(尤其是使用Unity引擎的游戏)、移动应用等。

对于编程初学者来说,C#是一个很好的入门选择,因为它的语法清晰、结构严谨,同时又有庞大的社区支持和丰富的学习资源。本指南将带你从零开始,逐步掌握C#的基础语法,即使你没有任何编程经验,也能轻松上手。

2. 开发环境搭建

在开始编写C#代码之前,你需要安装一个集成开发环境(IDE)。对于C#开发,最常用的IDE是Visual Studio。

安装Visual Studio

  1. 访问Visual Studio官网:https://visualstudio.microsoft.com/
  2. 下载Visual Studio Community版本(免费且功能齐全)
  3. 运行安装程序,在”工作负载”选项卡中,选择”.NET桌面开发”
  4. 点击安装按钮,等待安装完成

创建你的第一个C#项目

  1. 打开Visual Studio
  2. 点击”创建新项目”
  3. 选择”控制台应用”模板
  4. 为项目命名(例如”HelloWorld”),选择保存位置
  5. 点击”创建”按钮

现在,你已经准备好开始编写C#代码了!

3. C#程序的基本结构

让我们从一个经典的”Hello World”程序开始,了解C#程序的基本结构。

using System; namespace HelloWorld { class Program { static void Main(string[] args) { Console.WriteLine("Hello, World!"); } } } 

代码解析

  • using System; - 引入System命名空间,包含基本的输入输出功能
  • namespace HelloWorld - 定义一个名为HelloWorld的命名空间,用于组织代码
  • class Program - 定义一个名为Program的类,C#程序的基本组成单位
  • static void Main(string[] args) - 程序的入口点,程序从这里开始执行
    • static - 表示这个方法属于类本身,而不是类的实例
    • void - 表示这个方法不返回任何值
    • Main - 方法名,是C#程序的入口点
    • string[] args - 参数,用于接收命令行参数
  • Console.WriteLine("Hello, World!"); - 在控制台输出文本

运行程序

在Visual Studio中,你可以通过以下方式运行程序:

  • 按F5键(开始调试)
  • 点击工具栏上的”开始”按钮(绿色三角形)
  • 使用菜单:调试 -> 开始调试

你将在控制台窗口看到输出:”Hello, World!”

4. 变量与数据类型

在编程中,变量是用来存储数据的容器。每个变量都有一个特定的数据类型,决定了它可以存储什么类型的数据以及如何操作这些数据。

基本数据类型

C#提供了多种基本数据类型,以下是最常用的几种:

数据类型描述示例范围
int整数int age = 25;-2,147,483,648 到 2,147,483,647
double双精度浮点数double price = 19.99;±5.0 × 10^−324 到 ±1.7 × 10^308
float单精度浮点数float temperature = 36.6f;±1.5 × 10^−45 到 ±3.4 × 10^38
char单个字符char grade = ‘A’;U+0000 到 U+ffff
bool布尔值(真/假)bool isPassed = true;true 或 false
string字符串string name = “John”;任意长度的字符序列

变量声明与初始化

在C#中,你可以先声明变量,然后再赋值,也可以在声明的同时初始化:

// 先声明,后赋值 int age; age = 25; // 声明的同时初始化 string name = "John Doe"; double height = 175.5; bool isStudent = true; 

变量命名规则

在C#中,变量命名需要遵循以下规则:

  • 变量名必须以字母、下划线(_)或@符号开头
  • 变量名可以包含字母、数字、下划线
  • 变量名不能是C#关键字(如int, class, void等)
  • 变量名区分大小写(age和Age是不同的变量)

良好的命名习惯:

  • 使用有意义的名称(如studentAge而不是a)
  • 使用驼峰命名法(第一个单词小写,后续单词首字母大写,如firstName)
  • 避免使用缩写(除非是广泛接受的缩写)

类型推断

C#允许使用var关键字让编译器自动推断变量的类型:

var age = 25; // 编译器推断age为int类型 var name = "John"; // 编译器推断name为string类型 var price = 19.99; // 编译器推断price为double类型 

注意:使用var时必须在声明的同时初始化,因为编译器需要根据初始值来确定类型。

常量

如果你有一个值在程序运行期间不会改变,可以将其声明为常量:

const double PI = 3.14159; const int DAYS_IN_WEEK = 7; 

常量一旦声明就不能修改,尝试修改常量值会导致编译错误。

类型转换

有时,你需要将一种数据类型的值转换为另一种数据类型。C#中有两种类型的转换:隐式转换和显式转换。

隐式转换

当转换是安全的(不会导致数据丢失)时,C#会自动进行隐式转换:

int intValue = 100; long longValue = intValue; // int可以隐式转换为long,因为long的范围更大 float floatValue = 10.5f; double doubleValue = floatValue; // float可以隐式转换为double 

显式转换(强制转换)

当转换可能导致数据丢失时,需要进行显式转换:

double doubleValue = 10.8; int intValue = (int)doubleValue; // intValue将为10,小数部分被截断 char charValue = 'A'; int asciiValue = (int)charValue; // asciiValue将为65 

使用Convert类进行转换

C#提供了Convert类,可以在各种基本数据类型之间进行转换:

string strNumber = "123"; int number = Convert.ToInt32(strNumber); string strDouble = "19.99"; double price = Convert.ToDouble(strDouble); int age = 25; string strAge = Convert.ToString(age); 

使用Parse方法

许多数据类型提供了Parse方法,可以将字符串转换为该类型:

string strNumber = "123"; int number = int.Parse(strNumber); string strDouble = "19.99"; double price = double.Parse(strDouble); 

注意:如果字符串格式不正确,Parse方法会抛出异常。为了避免这种情况,可以使用TryParse方法:

string strNumber = "abc"; int number; if (int.TryParse(strNumber, out number)) { Console.WriteLine("转换成功: " + number); } else { Console.WriteLine("转换失败"); } 

5. 运算符

运算符是用于执行操作的符号。C#提供了多种运算符,包括算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符等。

算术运算符

算术运算符用于执行基本的数学运算:

运算符描述示例
+加法int sum = 5 + 3; // 结果为8
-减法int difference = 5 - 3; // 结果为2
*乘法int product = 5 * 3; // 结果为15
/除法int quotient = 15 / 3; // 结果为5
%取模(求余)int remainder = 10 % 3; // 结果为1
++自增int i = 5; i++; // i变为6
自减int i = 5; i–; // i变为4

自增和自减运算符可以放在变量的前面(前缀)或后面(后缀),它们的效果不同:

int x = 5; int y = x++; // y为5,x为6(先赋值,后自增) int a = 5; int b = ++a; // b为6,a为6(先自增,后赋值) 

关系运算符

关系运算符用于比较两个值,结果是一个布尔值(true或false):

运算符描述示例
==等于bool result = (5 == 3); // 结果为false
!=不等于bool result = (5 != 3); // 结果为true
>大于bool result = (5 > 3); // 结果为true
<小于bool result = (5 < 3); // 结果为false
>=大于等于bool result = (5 >= 3); // 结果为true
<=小于等于bool result = (5 <= 3); // 结果为false

逻辑运算符

逻辑运算符用于组合多个条件,结果也是一个布尔值:

运算符描述示例
&&逻辑与bool result = (true && false); // 结果为false
||逻辑或bool result = (true || false); // 结果为true
!逻辑非bool result = !true; // 结果为false

短路求值:&&||运算符具有短路特性,即如果第一个操作数已经可以确定整个表达式的结果,就不会计算第二个操作数。

int x = 5; bool result = (x > 10) && (x++ < 20); // x > 10为false,所以不会计算x++ < 20,x仍为5 

位运算符

位运算符直接对整数的二进制位进行操作:

运算符描述示例
&按位与int result = 5 & 3; // 结果为1
|按位或int result = 5 | 3; // 结果为7
^按位异或int result = 5 ^ 3; // 结果为6
~按位取反int result = ~5; // 结果为-6
<<左移int result = 5 << 1; // 结果为10
>>右移int result = 5 >> 1; // 结果为2

赋值运算符

赋值运算符用于给变量赋值:

运算符描述示例
=简单赋值int x = 5;
+=加法赋值int x = 5; x += 3; // x变为8
-=减法赋值int x = 5; x -= 3; // x变为2
*=乘法赋值int x = 5; x *= 3; // x变为15
/=除法赋值int x = 15; x /= 3; // x变为5
%=取模赋值int x = 10; x %= 3; // x变为1
&=按位与赋值int x = 5; x &= 3; // x变为1
|=按位或赋值int x = 5; x |= 3; // x变为7
^=按位异或赋值int x = 5; x ^= 3; // x变为6
<<=左移赋值int x = 5; x <<= 1; // x变为10
>>=右移赋值int x = 5; x >>= 1; // x变为2

运算符优先级

当一个表达式中有多个运算符时,运算符的优先级决定了计算的顺序。以下是C#中运算符的优先级(从高到低):

  1. 括号:()
  2. 自增/自减:++, –(作为前缀)
  3. 乘法/除法/取模:*, /, %
  4. 加法/减法:+, -
  5. 关系运算符:<, >, <=, >=
  6. 相等性运算符:==, !=
  7. 按位与:&
  8. 按位异或:^
  9. 按位或:|
  10. 逻辑与:&&
  11. 逻辑或:||
  12. 赋值运算符:=, +=, -=, *=, /=, %=等

可以使用括号改变运算的优先级:

int result = 5 + 3 * 2; // 结果为11,因为*优先级高于+ int result = (5 + 3) * 2; // 结果为16,因为括号改变了优先级 

6. 控制流语句

控制流语句允许你根据条件控制程序的执行路径。C#提供了多种控制流语句,包括条件语句和循环语句。

条件语句

if语句

if语句用于在条件为true时执行一段代码:

int age = 18; if (age >= 18) { Console.WriteLine("你是成年人"); } 

if-else语句

if-else语句在条件为true时执行一段代码,为false时执行另一段代码:

int age = 16; if (age >= 18) { Console.WriteLine("你是成年人"); } else { Console.WriteLine("你是未成年人"); } 

if-else if-else语句

当有多个条件需要判断时,可以使用if-else if-else语句:

int score = 85; if (score >= 90) { Console.WriteLine("优秀"); } else if (score >= 80) { Console.WriteLine("良好"); } else if (score >= 60) { Console.WriteLine("及格"); } else { Console.WriteLine("不及格"); } 

switch语句

switch语句用于根据变量的值执行不同的代码块,特别适合多分支情况:

char grade = 'B'; switch (grade) { case 'A': Console.WriteLine("优秀"); break; case 'B': Console.WriteLine("良好"); break; case 'C': Console.WriteLine("及格"); break; case 'D': Console.WriteLine("不及格"); break; default: Console.WriteLine("无效的成绩"); break; } 

C# 7.0及以上版本支持switch表达式,可以更简洁地实现相同的功能:

char grade = 'B'; string message = grade switch { 'A' => "优秀", 'B' => "良好", 'C' => "及格", 'D' => "不及格", _ => "无效的成绩" }; Console.WriteLine(message); 

三元运算符

三元运算符?:if-else语句的简洁形式,适用于简单的条件判断:

int age = 18; string message = (age >= 18) ? "你是成年人" : "你是未成年人"; Console.WriteLine(message); 

循环语句

for循环

for循环用于重复执行一段代码特定次数:

for (int i = 1; i <= 5; i++) { Console.WriteLine("这是第 " + i + " 次循环"); } 

for循环由三部分组成:

  1. 初始化:int i = 1,设置循环变量的初始值
  2. 条件:i <= 5,每次循环前检查的条件,如果为true则继续循环
  3. 迭代:i++,每次循环后执行的操作,通常用于更新循环变量

while循环

while循环在条件为true时重复执行一段代码:

int count = 1; while (count <= 5) { Console.WriteLine("这是第 " + count + " 次循环"); count++; } 

注意:如果条件始终为true,while循环会变成无限循环,应避免这种情况。

do-while循环

do-while循环至少执行一次,然后在条件为true时继续执行:

int count = 1; do { Console.WriteLine("这是第 " + count + " 次循环"); count++; } while (count <= 5); 

do-while循环与while循环的区别在于,do-while循环先执行代码,再检查条件,因此至少执行一次。

foreach循环

foreach循环用于遍历集合或数组中的每个元素:

string[] fruits = { "苹果", "香蕉", "橙子" }; foreach (string fruit in fruits) { Console.WriteLine(fruit); } 

跳转语句

break语句

break语句用于立即退出循环或switch语句:

for (int i = 1; i <= 10; i++) { if (i == 6) { break; // 当i等于6时退出循环 } Console.WriteLine(i); } // 输出:1 2 3 4 5 

continue语句

continue语句用于跳过当前循环的剩余部分,直接进入下一次循环:

for (int i = 1; i <= 10; i++) { if (i % 2 == 0) { continue; // 跳过偶数 } Console.WriteLine(i); } // 输出:1 3 5 7 9 

goto语句

goto语句允许程序直接跳转到指定的标签处。虽然C#支持goto语句,但通常不推荐使用,因为它会使代码结构混乱,难以维护:

int i = 0; start: Console.WriteLine(i); i++; if (i < 5) { goto start; } 

return语句

return语句用于退出方法,并可以返回一个值(如果方法的返回类型不是void):

int Add(int a, int b) { return a + b; } void PrintMessage(string message) { Console.WriteLine(message); return; // 在void方法中,return是可选的 } 

7. 数组与集合

数组和集合是用于存储多个数据项的数据结构。它们允许你以有序的方式组织和管理数据。

数组

数组是一个固定大小的序列,其中所有元素都具有相同的类型。

一维数组

声明和初始化一维数组的几种方式:

// 声明并指定大小 int[] numbers = new int[5]; // 声明并初始化 int[] numbers = new int[] { 1, 2, 3, 4, 5 }; // 简化形式 int[] numbers = { 1, 2, 3, 4, 5 }; 

访问数组元素:

int[] numbers = { 1, 2, 3, 4, 5 }; Console.WriteLine(numbers[0]); // 输出第一个元素:1 numbers[0] = 10; // 修改第一个元素的值 Console.WriteLine(numbers[0]); // 输出:10 

遍历数组:

int[] numbers = { 1, 2, 3, 4, 5 }; // 使用for循环 for (int i = 0; i < numbers.Length; i++) { Console.WriteLine(numbers[i]); } // 使用foreach循环 foreach (int number in numbers) { Console.WriteLine(number); } 

多维数组

C#支持多维数组,最常见的是二维数组:

// 声明并初始化二维数组 int[,] matrix = new int[3, 4]; // 声明并初始化二维数组(带初始值) int[,] matrix = new int[,] { { 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 } }; 

访问二维数组元素:

int[,] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } }; Console.WriteLine(matrix[0, 0]); // 输出第一行第一列的元素:1 matrix[0, 0] = 10; // 修改第一行第一列的值 Console.WriteLine(matrix[0, 0]); // 输出:10 

遍历二维数组:

int[,] matrix = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } }; // 使用嵌套for循环 for (int i = 0; i < matrix.GetLength(0); i++) { for (int j = 0; j < matrix.GetLength(1); j++) { Console.Write(matrix[i, j] + "t"); } Console.WriteLine(); } 

交错数组

交错数组是数组的数组,其中每个元素都是一个数组,这些数组的长度可以不同:

// 声明交错数组 int[][] jaggedArray = new int[3][]; // 初始化每个元素(数组) jaggedArray[0] = new int[] { 1, 2 }; jaggedArray[1] = new int[] { 3, 4, 5 }; jaggedArray[2] = new int[] { 6, 7, 8, 9 }; // 简化形式 int[][] jaggedArray = { new int[] { 1, 2 }, new int[] { 3, 4, 5 }, new int[] { 6, 7, 8, 9 } }; 

访问交错数组元素:

int[][] jaggedArray = { new int[] { 1, 2 }, new int[] { 3, 4, 5 }, new int[] { 6, 7, 8, 9 } }; Console.WriteLine(jaggedArray[0][0]); // 输出第一个数组的第一个元素:1 jaggedArray[0][0] = 10; // 修改第一个数组的第一个元素的值 Console.WriteLine(jaggedArray[0][0]); // 输出:10 

遍历交错数组:

int[][] jaggedArray = { new int[] { 1, 2 }, new int[] { 3, 4, 5 }, new int[] { 6, 7, 8, 9 } }; // 使用嵌套循环 for (int i = 0; i < jaggedArray.Length; i++) { for (int j = 0; j < jaggedArray[i].Length; j++) { Console.Write(jaggedArray[i][j] + " "); } Console.WriteLine(); } 

常用集合

除了数组,C#还提供了多种集合类,它们位于System.CollectionsSystem.Collections.Generic命名空间中。

List

List<T>是一个动态数组,可以根据需要自动调整大小:

using System.Collections.Generic; // 创建List List<string> fruits = new List<string>(); // 添加元素 fruits.Add("苹果"); fruits.Add("香蕉"); fruits.Add("橙子"); // 访问元素 Console.WriteLine(fruits[0]); // 输出:苹果 // 修改元素 fruits[0] = "葡萄"; Console.WriteLine(fruits[0]); // 输出:葡萄 // 删除元素 fruits.Remove("香蕉"); // 删除指定元素 fruits.RemoveAt(0); // 删除指定索引的元素 // 遍历List foreach (string fruit in fruits) { Console.WriteLine(fruit); } // 获取元素数量 Console.WriteLine("水果数量: " + fruits.Count); 

Dictionary

Dictionary<TKey, TValue>是一个键值对集合,通过键来访问值:

using System.Collections.Generic; // 创建Dictionary Dictionary<string, int> ages = new Dictionary<string, int>(); // 添加键值对 ages.Add("张三", 25); ages.Add("李四", 30); ages.Add("王五", 35); // 访问值 Console.WriteLine("张三的年龄: " + ages["张三"]); // 修改值 ages["张三"] = 26; Console.WriteLine("张三的年龄: " + ages["张三"]); // 检查键是否存在 if (ages.ContainsKey("张三")) { Console.WriteLine("张三的年龄: " + ages["张三"]); } // 删除键值对 ages.Remove("李四"); // 遍历Dictionary foreach (KeyValuePair<string, int> pair in ages) { Console.WriteLine(pair.Key + ": " + pair.Value); } 

Queue

Queue<T>是一个先进先出(FIFO)集合:

using System.Collections.Generic; // 创建Queue Queue<string> queue = new Queue<string>(); // 添加元素(入队) queue.Enqueue("第一个"); queue.Enqueue("第二个"); queue.Enqueue("第三个"); // 查看队首元素(不移除) Console.WriteLine("队首元素: " + queue.Peek()); // 移除并返回队首元素(出队) string item = queue.Dequeue(); Console.WriteLine("出队元素: " + item); // 遍历Queue foreach (string qItem in queue) { Console.WriteLine(qItem); } 

Stack

Stack<T>是一个后进先出(LIFO)集合:

using System.Collections.Generic; // 创建Stack Stack<string> stack = new Stack<string>(); // 添加元素(入栈) stack.Push("第一个"); stack.Push("第二个"); stack.Push("第三个"); // 查看栈顶元素(不移除) Console.WriteLine("栈顶元素: " + stack.Peek()); // 移除并返回栈顶元素(出栈) string item = stack.Pop(); Console.WriteLine("出栈元素: " + item); // 遍历Stack foreach (string sItem in stack) { Console.WriteLine(sItem); } 

8. 方法的定义与使用

方法(也称为函数)是一段可重用的代码,用于执行特定的任务。方法可以提高代码的模块化和可重用性。

方法的定义

方法的基本语法如下:

访问修饰符 返回类型 方法名(参数列表) { // 方法体 return 返回值; // 如果返回类型不是void } 
  • 访问修饰符:如publicprivateprotected等,指定方法的可访问性
  • 返回类型:方法返回的值的类型,如果方法不返回值,则使用void
  • 方法名:方法的名称,遵循标识符命名规则
  • 参数列表:方法接收的输入参数,多个参数用逗号分隔
  • 方法体:包含执行任务的代码
  • return语句:返回结果并退出方法,如果返回类型是void,可以省略return语句或使用return;不带值

无参数无返回值的方法

public void SayHello() { Console.WriteLine("Hello, World!"); } 

带参数无返回值的方法

public void SayHelloTo(string name) { Console.WriteLine("Hello, " + name + "!"); } 

带参数有返回值的方法

public int Add(int a, int b) { return a + b; } 

参数传递

C#中有两种参数传递方式:值传递和引用传递。

值传递

默认情况下,参数是通过值传递的,即方法接收的是参数的副本,对参数的修改不会影响原始值:

public void ModifyValue(int x) { x = 100; } int number = 5; ModifyValue(number); Console.WriteLine(number); // 输出:5,原始值不受影响 

引用传递

使用ref关键字可以通过引用传递参数,这样方法可以修改原始值:

public void ModifyRef(ref int x) { x = 100; } int number = 5; ModifyRef(ref number); Console.WriteLine(number); // 输出:100,原始值被修改 

注意:使用ref参数时,调用方法时也必须使用ref关键字。

输出参数

使用out关键字可以定义输出参数,用于从方法返回多个值:

public void Divide(int dividend, int divisor, out int quotient, out int remainder) { quotient = dividend / divisor; remainder = dividend % divisor; } int quotient, remainder; Divide(10, 3, out quotient, out remainder); Console.WriteLine("商: " + quotient + ", 余数: " + remainder); // 输出:商: 3, 余数: 1 

注意:使用out参数时,调用方法时也必须使用out关键字,并且方法必须为out参数赋值。

参数数组

使用params关键字可以定义可变数量的参数:

public int CalculateSum(params int[] numbers) { int sum = 0; foreach (int number in numbers) { sum += number; } return sum; } int sum1 = CalculateSum(1, 2, 3); // 传递三个参数 int sum2 = CalculateSum(1, 2, 3, 4, 5); // 传递五个参数 int sum3 = CalculateSum(); // 不传递参数 Console.WriteLine(sum1); // 输出:6 Console.WriteLine(sum2); // 输出:15 Console.WriteLine(sum3); // 输出:0 

方法重载

方法重载允许在同一个类中定义多个同名的方法,但参数列表必须不同(参数个数、类型或顺序不同):

public int Add(int a, int b) { return a + b; } public double Add(double a, double b) { return a + b; } public int Add(int a, int b, int c) { return a + b + c; } int sum1 = Add(1, 2); // 调用第一个Add方法 double sum2 = Add(1.5, 2.5); // 调用第二个Add方法 int sum3 = Add(1, 2, 3); // 调用第三个Add方法 

递归方法

递归方法是指方法调用自身的情况。递归通常用于解决可以分解为相似子问题的问题:

public int Factorial(int n) { if (n == 0 || n == 1) { return 1; } else { return n * Factorial(n - 1); } } int result = Factorial(5); Console.WriteLine(result); // 输出:120 

注意:递归方法必须有终止条件,否则会导致无限递归和栈溢出错误。

静态方法与实例方法

静态方法

静态方法使用static关键字修饰,属于类本身,而不是类的实例。静态方法可以通过类名直接调用,不需要创建类的实例:

public class MathHelper { public static int Add(int a, int b) { return a + b; } } int sum = MathHelper.Add(5, 3); // 直接通过类名调用 Console.WriteLine(sum); // 输出:8 

实例方法

实例方法不属于static,必须通过类的实例来调用:

public class Calculator { public int Add(int a, int b) { return a + b; } } Calculator calculator = new Calculator(); // 创建实例 int sum = calculator.Add(5, 3); // 通过实例调用 Console.WriteLine(sum); // 输出:8 

9. 类与对象

C#是一种面向对象的编程语言,类和对象是面向对象编程的核心概念。

类的定义

类是一种用户定义的数据类型,它包含数据成员(字段、属性)和函数成员(方法、构造函数等):

public class Person { // 字段 private string name; private int age; // 构造函数 public Person(string name, int age) { this.name = name; this.age = age; } // 属性 public string Name { get { return name; } set { name = value; } } public int Age { get { return age; } set { age = value; } } // 方法 public void SayHello() { Console.WriteLine("Hello, my name is " + name + " and I am " + age + " years old."); } } 

对象的创建与使用

对象是类的实例,使用new关键字创建:

// 创建Person对象 Person person = new Person("张三", 25); // 访问属性 Console.WriteLine("姓名: " + person.Name); Console.WriteLine("年龄: " + person.Age); // 修改属性 person.Name = "李四"; person.Age = 30; // 调用方法 person.SayHello(); 

构造函数

构造函数是一种特殊的方法,用于在创建对象时初始化对象。构造函数的名称与类名相同,没有返回类型。

默认构造函数

如果没有显式定义构造函数,C#会提供一个默认的无参构造函数:

public class Person { private string name; private int age; // 默认构造函数 public Person() { name = "Unknown"; age = 0; } } Person person = new Person(); // 使用默认构造函数 

带参数的构造函数

可以定义带参数的构造函数,以便在创建对象时提供初始值:

public class Person { private string name; private int age; // 带参数的构造函数 public Person(string name, int age) { this.name = name; this.age = age; } } Person person = new Person("张三", 25); // 使用带参数的构造函数 

构造函数重载

可以定义多个构造函数,只要它们的参数列表不同:

public class Person { private string name; private int age; // 默认构造函数 public Person() { name = "Unknown"; age = 0; } // 带一个参数的构造函数 public Person(string name) { this.name = name; age = 0; } // 带两个参数的构造函数 public Person(string name, int age) { this.name = name; this.age = age; } } Person person1 = new Person(); Person person2 = new Person("张三"); Person person3 = new Person("张三", 25); 

构造函数链

可以使用this关键字在一个构造函数中调用另一个构造函数:

public class Person { private string name; private int age; public Person() : this("Unknown", 0) { } public Person(string name) : this(name, 0) { } public Person(string name, int age) { this.name = name; this.age = age; } } 

属性

属性是类的成员,它提供了一种灵活的机制来读取、写入或计算私有字段的值。属性使用访问器(get和set)来定义。

只读属性

只有get访问器的属性是只读属性:

public class Circle { private double radius; public Circle(double radius) { this.radius = radius; } public double Radius { get { return radius; } } public double Area { get { return Math.PI * radius * radius; } } } Circle circle = new Circle(5); Console.WriteLine("半径: " + circle.Radius); Console.WriteLine("面积: " + circle.Area); // circle.Radius = 10; // 错误,Radius是只读属性 

只写属性

只有set访问器的属性是只写属性:

public class Password { private string password; public string Value { set { password = value; } } public bool Check(string input) { return input == password; } } Password pwd = new Password(); pwd.Value = "secret123"; // 设置密码 // Console.WriteLine(pwd.Value); // 错误,Value是只写属性 Console.WriteLine(pwd.Check("secret123")); // 输出:True Console.WriteLine(pwd.Check("wrong")); // 输出:False 

自动实现的属性

如果属性不需要额外的逻辑,可以使用自动实现的属性:

public class Person { public string Name { get; set; } public int Age { get; set; } } Person person = new Person(); person.Name = "张三"; person.Age = 25; Console.WriteLine("姓名: " + person.Name); Console.WriteLine("年龄: " + person.Age); 

属性初始化器

C# 6.0及以上版本支持属性初始化器,可以在声明属性时指定初始值:

public class Person { public string Name { get; set; } = "Unknown"; public int Age { get; set; } = 0; } Person person = new Person(); Console.WriteLine("姓名: " + person.Name); // 输出:Unknown Console.WriteLine("年龄: " + person.Age); // 输出:0 

访问修饰符

访问修饰符用于指定类、字段、属性、方法等成员的可访问性:

访问修饰符描述
public可以从任何地方访问
private只能在定义它的类内部访问
protected只能在定义它的类或派生类中访问
internal只能在同一程序集内访问
protected internal可以在同一程序集或派生类中访问
public class Person { // 私有字段,只能在Person类内部访问 private string name; // 公共属性,可以从任何地方访问 public string Name { get { return name; } set { name = value; } } // 受保护的方法,只能在Person类或其派生类中访问 protected void Display() { Console.WriteLine("Name: " + name); } // 内部属性,只能在同一程序集内访问 internal int Age { get; set; } } 

静态成员

使用static关键字修饰的成员属于类本身,而不是类的实例。静态成员可以通过类名直接访问,不需要创建类的实例。

静态字段

public class Counter { public static int count = 0; public Counter() { count++; // 每次创建Counter对象时,count增加1 } } Counter c1 = new Counter(); Counter c2 = new Counter(); Counter c3 = new Counter(); Console.WriteLine(Counter.count); // 输出:3 

静态属性

public class Circle { private static double _pi = 3.14159; public static double PI { get { return _pi; } } } Console.WriteLine("PI的值: " + Circle.PI); 

静态方法

public class MathHelper { public static int Add(int a, int b) { return a + b; } public static int Multiply(int a, int b) { return a * b; } } int sum = MathHelper.Add(5, 3); int product = MathHelper.Multiply(5, 3); Console.WriteLine("和: " + sum); // 输出:8 Console.WriteLine("积: " + product); // 输出:15 

静态类

使用static关键字修饰的类只能包含静态成员,不能被实例化:

public static class ConfigurationManager { private static string _connectionString; public static string ConnectionString { get { return _connectionString; } set { _connectionString = value; } } public static void LoadConfiguration() { // 加载配置的代码 } } // 使用静态类 ConfigurationManager.ConnectionString = "Server=myServer;Database=myDB;"; ConfigurationManager.LoadConfiguration(); // ConfigurationManager config = new ConfigurationManager(); // 错误,不能实例化静态类 

this关键字

this关键字引用当前类的实例,可以用于:

  • 区分类的字段和参数/局部变量
  • 调用当前类的其他构造函数
  • 作为参数传递给其他方法
public class Person { private string name; private int age; public Person(string name, int age) { // 使用this区分字段和参数 this.name = name; this.age = age; } public Person(string name) : this(name, 0) { // 调用其他构造函数 } public void PrintDetails() { // 将当前实例作为参数传递 Printer.Print(this); } } public static class Printer { public static void Print(Person person) { Console.WriteLine("Name: " + person.Name + ", Age: " + person.Age); } } 

10. 继承与多态

继承和多态是面向对象编程的重要特性,它们允许创建基于现有类的新类,并实现”一个接口,多种方法”的编程模式。

继承

继承允许一个类(派生类)继承另一个类(基类)的字段、属性和方法。派生类可以重用基类的代码,并可以添加新的成员或重写基类的成员。

基本语法

// 基类 public class Animal { protected string name; public Animal(string name) { this.name = name; } public void Eat() { Console.WriteLine(name + " is eating."); } public virtual void Sleep() { Console.WriteLine(name + " is sleeping."); } } // 派生类 public class Dog : Animal { public Dog(string name) : base(name) { } public void Bark() { Console.WriteLine(name + " is barking."); } public override void Sleep() { Console.WriteLine(name + " is sleeping peacefully."); } } 

使用继承

Dog dog = new Dog("Buddy"); dog.Eat(); // 继承自Animal类的方法 dog.Bark(); // Dog类自己的方法 dog.Sleep(); // 重写的方法 

构造函数和继承

派生类的构造函数可以调用基类的构造函数,使用base关键字:

public class Animal { protected string name; public Animal(string name) { this.name = name; } } public class Dog : Animal { private string breed; public Dog(string name, string breed) : base(name) { this.breed = breed; } } 

访问基类成员

使用base关键字可以访问基类的成员:

public class Animal { protected string name; public Animal(string name) { this.name = name; } public virtual void Sleep() { Console.WriteLine(name + " is sleeping."); } } public class Dog : Animal { public Dog(string name) : base(name) { } public override void Sleep() { base.Sleep(); // 调用基类的Sleep方法 Console.WriteLine(name + " is dreaming about chasing cats."); } } 

多态

多态允许使用基类引用指向派生类对象,并在运行时调用适当的重写方法。

虚方法和方法重写

使用virtual关键字标记的方法可以在派生类中被重写,使用override关键字重写方法:

public class Shape { public virtual void Draw() { Console.WriteLine("Drawing a shape."); } } public class Circle : Shape { public override void Draw() { Console.WriteLine("Drawing a circle."); } } public class Rectangle : Shape { public override void Draw() { Console.WriteLine("Drawing a rectangle."); } } 

使用多态

Shape[] shapes = new Shape[3]; shapes[0] = new Shape(); shapes[1] = new Circle(); shapes[2] = new Rectangle(); foreach (Shape shape in shapes) { shape.Draw(); // 根据实际对象类型调用适当的Draw方法 } 

输出:

Drawing a shape. Drawing a circle. Drawing a rectangle. 

抽象类和抽象方法

抽象类是不能被实例化的类,它只能作为其他类的基类。抽象方法是没有实现的方法,必须在非抽象派生类中被重写。

public abstract class Shape { public abstract void Draw(); // 抽象方法,没有实现 public void Display() { Console.WriteLine("This is a shape."); } } public class Circle : Shape { public override void Draw() { Console.WriteLine("Drawing a circle."); } } public class Rectangle : Shape { public override void Draw() { Console.WriteLine("Drawing a rectangle."); } } 

使用抽象类:

// Shape shape = new Shape(); // 错误,不能实例化抽象类 Shape circle = new Circle(); Shape rectangle = new Rectangle(); circle.Draw(); // 输出:Drawing a circle. rectangle.Draw(); // 输出:Drawing a rectangle. circle.Display(); // 输出:This is a shape. rectangle.Display(); // 输出:This is a shape. 

密封类和密封方法

密封类是不能被继承的类,使用sealed关键字标记。密封方法是不能被重写的方法,也使用sealed关键字标记。

public sealed class FinalClass { // 类的实现 } // public class DerivedClass : FinalClass { } // 错误,不能继承密封类 public class BaseClass { public virtual void Method() { Console.WriteLine("BaseClass.Method"); } } public class DerivedClass : BaseClass { public sealed override void Method() { Console.WriteLine("DerivedClass.Method"); } } public class FurtherDerivedClass : DerivedClass { // public override void Method() { } // 错误,不能重写密封方法 } 

接口

接口定义了一组成员(方法、属性、事件和索引器),但不提供实现。类或结构可以实现接口,并提供接口成员的具体实现。

public interface IDrawable { void Draw(); } public interface IResizable { void Resize(int width, int height); } public class Rectangle : IDrawable, IResizable { private int width; private int height; public Rectangle(int width, int height) { this.width = width; this.height = height; } public void Draw() { Console.WriteLine("Drawing a rectangle with width " + width + " and height " + height); } public void Resize(int width, int height) { this.width = width; this.height = height; Console.WriteLine("Resized to width " + width + " and height " + height); } } 

使用接口:

Rectangle rectangle = new Rectangle(10, 20); IDrawable drawable = rectangle; IResizable resizable = rectangle; drawable.Draw(); // 输出:Drawing a rectangle with width 10 and height 20 resizable.Resize(15, 25); // 输出:Resized to width 15 and height 25 

显式接口实现

如果一个类实现多个接口,而这些接口有相同签名的成员,可以使用显式接口实现来解决冲突:

public interface IControl { void Paint(); } public interface ISurface { void Paint(); } public class SampleClass : IControl, ISurface { // 显式接口实现 void IControl.Paint() { Console.WriteLine("IControl.Paint"); } void ISurface.Paint() { Console.WriteLine("ISurface.Paint"); } // 普通方法 public void Paint() { Console.WriteLine("SampleClass.Paint"); } } 

使用显式接口实现:

SampleClass sample = new SampleClass(); IControl control = sample; ISurface surface = sample; sample.Paint(); // 输出:SampleClass.Paint control.Paint(); // 输出:IControl.Paint surface.Paint(); // 输出:ISurface.Paint 

11. 异常处理

异常是在程序执行过程中发生的事件,它会中断正常的指令流。C#提供了强大的异常处理机制,使程序能够优雅地处理错误情况。

try-catch块

try-catch块用于捕获和处理异常:

try { // 可能引发异常的代码 int a = 10; int b = 0; int result = a / b; // 会引发DivideByZeroException Console.WriteLine("结果: " + result); } catch (DivideByZeroException ex) { // 处理特定异常 Console.WriteLine("错误: 不能除以零"); Console.WriteLine("异常信息: " + ex.Message); } catch (Exception ex) { // 处理其他异常 Console.WriteLine("发生错误: " + ex.Message); } 

try-catch-finally块

finally块包含的代码无论是否发生异常都会执行,通常用于释放资源:

try { // 可能引发异常的代码 int[] numbers = { 1, 2, 3 }; Console.WriteLine(numbers[5]); // 会引发IndexOutOfRangeException } catch (IndexOutOfRangeException ex) { // 处理异常 Console.WriteLine("错误: 数组索引超出范围"); } finally { // 无论是否发生异常都会执行的代码 Console.WriteLine("finally块已执行"); } 

using语句

using语句用于确保实现了IDisposable接口的对象在使用后被正确释放:

using (System.IO.StreamReader reader = new System.IO.StreamReader("file.txt")) { string content = reader.ReadToEnd(); Console.WriteLine(content); } // reader.Dispose()会自动调用 

throw语句

throw语句用于显式引发异常:

public void Divide(int a, int b) { if (b == 0) { throw new DivideByZeroException("除数不能为零"); } int result = a / b; Console.WriteLine("结果: " + result); } try { Divide(10, 0); } catch (DivideByZeroException ex) { Console.WriteLine("捕获异常: " + ex.Message); } 

自定义异常

可以创建自定义异常类,继承自Exception类或其派生类:

public class InvalidAgeException : Exception { public InvalidAgeException() : base("年龄无效") { } public InvalidAgeException(string message) : base(message) { } public InvalidAgeException(string message, Exception innerException) : base(message, innerException) { } } public class Person { private int age; public int Age { get { return age; } set { if (value < 0 || value > 150) { throw new InvalidAgeException("年龄必须在0到150之间"); } age = value; } } } try { Person person = new Person(); person.Age = 200; // 会引发InvalidAgeException } catch (InvalidAgeException ex) { Console.WriteLine("捕获异常: " + ex.Message); } 

常见异常类

C#提供了许多内置的异常类,以下是一些常见的异常类:

异常类描述
Exception所有异常的基类
ArgumentException方法的参数无效
ArgumentNullException参数为null
ArgumentOutOfRangeException参数超出范围
DivideByZeroException尝试除以零
IndexOutOfRangeException数组索引超出范围
InvalidCastException无效的类型转换
InvalidOperationException方法调用对对象当前状态无效
IOException发生I/O错误
NotSupportedException不支持的操作
NullReferenceException尝试引用null对象
OverflowException算术运算导致溢出
StackOverflowException执行堆栈溢出
TimeoutException操作超时

12. 文件操作

C#提供了丰富的类和方法用于文件和目录操作,这些类主要位于System.IO命名空间中。

文件读写

读取文本文件

using System; using System.IO; class Program { static void Main() { string filePath = "example.txt"; // 检查文件是否存在 if (File.Exists(filePath)) { // 读取所有文本 string content = File.ReadAllText(filePath); Console.WriteLine("文件内容:"); Console.WriteLine(content); // 读取所有行 string[] lines = File.ReadAllLines(filePath); Console.WriteLine("n文件行数: " + lines.Length); // 使用StreamReader逐行读取 Console.WriteLine("n使用StreamReader逐行读取:"); using (StreamReader reader = new StreamReader(filePath)) { string line; while ((line = reader.ReadLine()) != null) { Console.WriteLine(line); } } } else { Console.WriteLine("文件不存在: " + filePath); } } } 

写入文本文件

using System; using System.IO; class Program { static void Main() { string filePath = "output.txt"; // 写入所有文本(会覆盖现有文件) string content = "这是第一行。n这是第二行。n这是第三行。"; File.WriteAllText(filePath, content); Console.WriteLine("已写入文件: " + filePath); // 追加文本 string additionalContent = "n这是追加的行。"; File.AppendAllText(filePath, additionalContent); Console.WriteLine("已追加内容到文件: " + filePath); // 写入所有行 string[] lines = { "第一行", "第二行", "第三行" }; File.WriteAllLines("lines.txt", lines); Console.WriteLine("已写入行到文件: lines.txt"); // 使用StreamWriter写入 Console.WriteLine("n使用StreamWriter写入:"); using (StreamWriter writer = new StreamWriter("streamwriter.txt")) { writer.WriteLine("第一行"); writer.WriteLine("第二行"); writer.WriteLine("第三行"); } Console.WriteLine("已使用StreamWriter写入文件: streamwriter.txt"); } } 

二进制文件读写

using System; using System.IO; class Program { static void Main() { string filePath = "binarydata.bin"; // 写入二进制数据 using (BinaryWriter writer = new BinaryWriter(File.Open(filePath, FileMode.Create))) { writer.Write(123); // 整数 writer.Write(45.67); // 双精度浮点数 writer.Write("Hello, Binary World!"); // 字符串 writer.Write(true); // 布尔值 } Console.WriteLine("已写入二进制文件: " + filePath); // 读取二进制数据 using (BinaryReader reader = new BinaryReader(File.Open(filePath, FileMode.Open))) { int intValue = reader.ReadInt32(); double doubleValue = reader.ReadDouble(); string stringValue = reader.ReadString(); bool boolValue = reader.ReadBoolean(); Console.WriteLine("读取的整数值: " + intValue); Console.WriteLine("读取的双精度值: " + doubleValue); Console.WriteLine("读取的字符串值: " + stringValue); Console.WriteLine("读取的布尔值: " + boolValue); } } } 

文件和目录操作

using System; using System.IO; class Program { static void Main() { // 创建目录 string directoryPath = "TestDirectory"; if (!Directory.Exists(directoryPath)) { Directory.CreateDirectory(directoryPath); Console.WriteLine("已创建目录: " + directoryPath); } // 创建子目录 string subDirectoryPath = Path.Combine(directoryPath, "SubDirectory"); if (!Directory.Exists(subDirectoryPath)) { Directory.CreateDirectory(subDirectoryPath); Console.WriteLine("已创建子目录: " + subDirectoryPath); } // 创建文件 string filePath = Path.Combine(directoryPath, "testfile.txt"); if (!File.Exists(filePath)) { File.WriteAllText(filePath, "这是一个测试文件。"); Console.WriteLine("已创建文件: " + filePath); } // 复制文件 string copyPath = Path.Combine(directoryPath, "testfile_copy.txt"); if (!File.Exists(copyPath)) { File.Copy(filePath, copyPath); Console.WriteLine("已复制文件到: " + copyPath); } // 移动文件 string movePath = Path.Combine(subDirectoryPath, "testfile_moved.txt"); if (!File.Exists(movePath)) { File.Move(copyPath, movePath); Console.WriteLine("已移动文件到: " + movePath); } // 删除文件 if (File.Exists(movePath)) { File.Delete(movePath); Console.WriteLine("已删除文件: " + movePath); } // 获取文件信息 FileInfo fileInfo = new FileInfo(filePath); Console.WriteLine("n文件信息:"); Console.WriteLine("文件名: " + fileInfo.Name); Console.WriteLine("完整路径: " + fileInfo.FullName); Console.WriteLine("文件大小: " + fileInfo.Length + " 字节"); Console.WriteLine("创建时间: " + fileInfo.CreationTime); Console.WriteLine("最后访问时间: " + fileInfo.LastAccessTime); Console.WriteLine("最后修改时间: " + fileInfo.LastWriteTime); // 获取目录信息 DirectoryInfo directoryInfo = new DirectoryInfo(directoryPath); Console.WriteLine("n目录信息:"); Console.WriteLine("目录名: " + directoryInfo.Name); Console.WriteLine("完整路径: " + directoryInfo.FullName); Console.WriteLine("创建时间: " + directoryInfo.CreationTime); Console.WriteLine("最后访问时间: " + directoryInfo.LastAccessTime); Console.WriteLine("最后修改时间: " + directoryInfo.LastWriteTime); // 列出目录中的文件和子目录 Console.WriteLine("n目录内容:"); Console.WriteLine("文件:"); foreach (FileInfo file in directoryInfo.GetFiles()) { Console.WriteLine(" " + file.Name + " (" + file.Length + " 字节)"); } Console.WriteLine("子目录:"); foreach (DirectoryInfo dir in directoryInfo.GetDirectories()) { Console.WriteLine(" " + dir.Name); } // 删除目录(必须为空) if (Directory.Exists(subDirectoryPath)) { Directory.Delete(subDirectoryPath); Console.WriteLine("n已删除子目录: " + subDirectoryPath); } // 递归删除目录及其内容 if (Directory.Exists(directoryPath)) { Directory.Delete(directoryPath, true); Console.WriteLine("已删除目录及其内容: " + directoryPath); } } } 

文件路径处理

using System; using System.IO; class Program { static void Main() { // 获取当前目录 string currentDirectory = Directory.GetCurrentDirectory(); Console.WriteLine("当前目录: " + currentDirectory); // 组合路径 string fileName = "test.txt"; string fullPath = Path.Combine(currentDirectory, fileName); Console.WriteLine("完整路径: " + fullPath); // 获取路径信息 Console.WriteLine("n路径信息:"); Console.WriteLine("目录名: " + Path.GetDirectoryName(fullPath)); Console.WriteLine("文件名: " + Path.GetFileName(fullPath)); Console.WriteLine("不带扩展名的文件名: " + Path.GetFileNameWithoutExtension(fullPath)); Console.WriteLine("扩展名: " + Path.GetExtension(fullPath)); Console.WriteLine("根目录: " + Path.GetPathRoot(fullPath)); // 修改扩展名 string newPath = Path.ChangeExtension(fullPath, ".bak"); Console.WriteLine("n修改扩展名后的路径: " + newPath); // 获取临时文件名 string tempFileName = Path.GetTempFileName(); Console.WriteLine("临时文件名: " + tempFileName); // 获取临时路径 string tempPath = Path.GetTempPath(); Console.WriteLine("临时路径: " + tempPath); // 清理临时文件 File.Delete(tempFileName); Console.WriteLine("已删除临时文件: " + tempFileName); } } 

13. 实用示例

下面是一个综合运用前面知识的实用示例:一个简单的学生管理系统。

using System; using System.Collections.Generic; using System.IO; // 学生类 public class Student { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } public double Grade { get; set; } public Student(int id, string name, int age, double grade) { Id = id; Name = name; Age = age; Grade = grade; } public override string ToString() { return $"ID: {Id}, 姓名: {Name}, 年龄: {Age}, 成绩: {Grade}"; } } // 学生管理类 public class StudentManager { private List<Student> students; private int nextId; public StudentManager() { students = new List<Student>(); nextId = 1; } // 添加学生 public void AddStudent(string name, int age, double grade) { Student student = new Student(nextId, name, age, grade); students.Add(student); nextId++; Console.WriteLine("学生添加成功: " + student); } // 查找学生 public Student FindStudent(int id) { foreach (Student student in students) { if (student.Id == id) { return student; } } return null; } // 更新学生信息 public bool UpdateStudent(int id, string name, int age, double grade) { Student student = FindStudent(id); if (student != null) { student.Name = name; student.Age = age; student.Grade = grade; Console.WriteLine("学生信息更新成功: " + student); return true; } Console.WriteLine("未找到ID为 " + id + " 的学生"); return false; } // 删除学生 public bool DeleteStudent(int id) { Student student = FindStudent(id); if (student != null) { students.Remove(student); Console.WriteLine("学生删除成功: " + student); return true; } Console.WriteLine("未找到ID为 " + id + " 的学生"); return false; } // 显示所有学生 public void DisplayAllStudents() { if (students.Count == 0) { Console.WriteLine("没有学生记录"); return; } Console.WriteLine("所有学生信息:"); foreach (Student student in students) { Console.WriteLine(student); } } // 计算平均成绩 public double CalculateAverageGrade() { if (students.Count == 0) { return 0; } double sum = 0; foreach (Student student in students) { sum += student.Grade; } return sum / students.Count; } // 保存到文件 public void SaveToFile(string filePath) { try { using (StreamWriter writer = new StreamWriter(filePath)) { foreach (Student student in students) { writer.WriteLine($"{student.Id},{student.Name},{student.Age},{student.Grade}"); } } Console.WriteLine("数据已保存到文件: " + filePath); } catch (Exception ex) { Console.WriteLine("保存文件时出错: " + ex.Message); } } // 从文件加载 public void LoadFromFile(string filePath) { try { if (File.Exists(filePath)) { students.Clear(); nextId = 1; using (StreamReader reader = new StreamReader(filePath)) { string line; while ((line = reader.ReadLine()) != null) { string[] parts = line.Split(','); if (parts.Length == 4) { int id = int.Parse(parts[0]); string name = parts[1]; int age = int.Parse(parts[2]); double grade = double.Parse(parts[3]); Student student = new Student(id, name, age, grade); students.Add(student); if (id >= nextId) { nextId = id + 1; } } } } Console.WriteLine("数据已从文件加载: " + filePath); } else { Console.WriteLine("文件不存在: " + filePath); } } catch (Exception ex) { Console.WriteLine("加载文件时出错: " + ex.Message); } } } // 主程序 class Program { static void Main() { StudentManager manager = new StudentManager(); string filePath = "students.txt"; // 尝试从文件加载数据 manager.LoadFromFile(filePath); while (true) { Console.WriteLine("n学生管理系统"); Console.WriteLine("1. 添加学生"); Console.WriteLine("2. 查找学生"); Console.WriteLine("3. 更新学生信息"); Console.WriteLine("4. 删除学生"); Console.WriteLine("5. 显示所有学生"); Console.WriteLine("6. 计算平均成绩"); Console.WriteLine("7. 保存数据"); Console.WriteLine("8. 退出"); Console.Write("请选择操作: "); string choice = Console.ReadLine(); try { switch (choice) { case "1": // 添加学生 Console.Write("请输入学生姓名: "); string name = Console.ReadLine(); Console.Write("请输入学生年龄: "); int age = int.Parse(Console.ReadLine()); Console.Write("请输入学生成绩: "); double grade = double.Parse(Console.ReadLine()); manager.AddStudent(name, age, grade); break; case "2": // 查找学生 Console.Write("请输入学生ID: "); int findId = int.Parse(Console.ReadLine()); Student student = manager.FindStudent(findId); if (student != null) { Console.WriteLine("找到学生: " + student); } else { Console.WriteLine("未找到ID为 " + findId + " 的学生"); } break; case "3": // 更新学生信息 Console.Write("请输入学生ID: "); int updateId = int.Parse(Console.ReadLine()); Console.Write("请输入新的学生姓名: "); string newName = Console.ReadLine(); Console.Write("请输入新的学生年龄: "); int newAge = int.Parse(Console.ReadLine()); Console.Write("请输入新的学生成绩: "); double newGrade = double.Parse(Console.ReadLine()); manager.UpdateStudent(updateId, newName, newAge, newGrade); break; case "4": // 删除学生 Console.Write("请输入学生ID: "); int deleteId = int.Parse(Console.ReadLine()); manager.DeleteStudent(deleteId); break; case "5": // 显示所有学生 manager.DisplayAllStudents(); break; case "6": // 计算平均成绩 double average = manager.CalculateAverageGrade(); Console.WriteLine("学生平均成绩: " + average.ToString("F2")); break; case "7": // 保存数据 manager.SaveToFile(filePath); break; case "8": // 退出 Console.Write("是否保存数据? (y/n): "); string saveChoice = Console.ReadLine(); if (saveChoice.ToLower() == "y") { manager.SaveToFile(filePath); } Console.WriteLine("感谢使用学生管理系统,再见!"); return; default: Console.WriteLine("无效的选择,请重新输入"); break; } } catch (FormatException) { Console.WriteLine("输入格式错误,请输入有效的数字"); } catch (Exception ex) { Console.WriteLine("发生错误: " + ex.Message); } } } } 

这个学生管理系统演示了C#的许多基础语法和特性,包括:

  • 类和对象的定义与使用
  • 集合(List)的使用
  • 文件读写操作
  • 异常处理
  • 用户输入处理
  • 控制流语句(switch, while, if等)
  • 方法定义与调用

14. 学习资源与进阶路径

在线学习资源

  1. Microsoft Learn (原Microsoft Docs)

    • 网址:https://learn.microsoft.com/zh-cn/dotnet/csharp/
    • 描述:微软官方的C#文档,包含完整的语言参考、教程和API文档
  2. C# Station

    • 网址:https://www.csharp-station.com/
    • 描述:提供C#教程、文章和代码示例
  3. TutorialsTeacher

    • 网址:https://www.tutorialsteacher.com/csharp
    • 描述:提供结构化的C#教程,从基础到高级
  4. W3Schools C# Tutorial

    • 网址:https://www.w3schools.com/cs/
    • 描述:简单易懂的C#教程,适合初学者
  5. Codecademy - Learn C#

    • 网址:https://www.codecademy.com/learn/learn-c-sharp
    • 描述:交互式C#课程,通过实践学习

视频教程

  1. YouTube - C# 教程

    • 频道推荐:freeCodeCamp.org, Programming with Mosh, Brackeys
    • 描述:免费的C#视频教程,从基础到高级
  2. Udemy - C# 课程

    • 课程推荐:C# Basics for Beginners: Learn C# Fundamentals by Coding
    • 描述:付费但质量高的C#课程,通常有折扣
  3. Pluralsight - C# 路径

    • 网址:https://www.pluralsight.com/paths/csharp
    • 描述:结构化的C#学习路径,需要订阅

书籍推荐

  1. 《C# 9.0 in a Nutshell》 by Joseph Albahari and Ben Albahari

    • 描述:全面的C#参考书,适合有一定基础的开发者
  2. 《Head First C#》 by Andrew Stellman and Jennifer Greene

    • 描述:以视觉化和互动方式介绍C#,适合初学者
  3. 《C# 8.0 and .NET Core 3.0》 by Mark J. Price

    • 描述:现代C#和.NET Core开发实践指南
  4. 《Effective C#》 by Bill Wagner

    • 描述:提高C#编程技巧的最佳实践

实践项目

  1. 控制台应用程序

    • 描述:创建简单的控制台应用,如计算器、待办事项列表等
  2. Windows Forms 应用程序

    • 描述:开发带有图形用户界面的桌面应用程序
  3. ASP.NET Core Web 应用

    • 描述:构建Web应用程序和API
  4. Unity 游戏开发

    • 描述:使用C#和Unity引擎开发游戏

进阶学习路径

  1. 深入学习C#语言特性

    • 主题:LINQ、委托、事件、异步编程、反射等
  2. 学习.NET框架

    • 主题:.NET Core/.NET 5+、ASP.NET Core、Entity Framework Core等
  3. 设计模式

    • 主题:单例模式、工厂模式、观察者模式等常见设计模式
  4. 软件架构

    • 主题:分层架构、微服务、领域驱动设计等
  5. 性能优化

    • 主题:内存管理、算法优化、并发编程等

社区与支持

  1. Stack Overflow

    • 网址:https://stackoverflow.com/
    • 描述:程序员问答社区,可以提问和查找C#相关问题
  2. Reddit - r/csharp

    • 网址:https://www.reddit.com/r/csharp/
    • 描述:C#相关的讨论社区
  3. GitHub

    • 网址:https://github.com/
    • 描述:查找开源C#项目,参与贡献
  4. Microsoft Q&A

    • 网址:https://learn.microsoft.com/en-us/answers/products/dotnet
    • 描述:微软官方的技术问答社区

通过本指南的学习,你已经掌握了C#的基础语法和核心概念。编程是一个不断学习和实践的过程,希望你能继续深入探索C#和.NET的世界,成为一名优秀的开发者!