快速了解C#: 核心概念解析
欢迎来到C#的世界!无论您是编程新手,还是拥有其他语言(如Java, C++, Python)经验的开发者,希望快速掌握C#这门强大且用途广泛的语言,本文将为您提供一条清晰的路径。C#由微软开发,是一种现代、面向对象、类型安全的编程语言,广泛应用于构建各种类型的应用程序,从桌面应用、Web服务、游戏开发(Unity)到移动应用(Xamarin/MAUI)和云服务(Azure)。
要快速入门C#,理解其核心概念至关重要。本文将深入浅出地解析这些基石,帮助您建立坚实的C#知识体系。我们将从C#与.NET平台的关系讲起,逐步深入到语言的基本语法、数据类型、控制结构、面向对象编程的核心思想,以及一些重要的特性。
1. C#与.NET平台:共生关系
理解C#之前,首先需要了解它所依赖的平台——.NET。C#不是孤立存在的,它是在.NET生态系统中运行的。
- .NET是什么? .NET是一个免费、开源的开发平台,它包含了构建各种应用所需的运行时环境(CLR)、基础类库(BCL)和开发工具。你可以把.NET理解为一个巨大的工具箱和运行环境,C#代码就在这个环境中被构建、编译和执行。
- 公共语言运行时 (CLR): 这是.NET的心脏。C#代码首先被编译成一种中间语言(Intermediate Language, IL),而不是直接编译成机器码。CLR负责在运行时将IL代码通过即时编译(Just-In-Time compilation, JIT)转换成机器码并在特定操作系统上执行。这使得.NET具有跨平台的能力(随着.NET Core/.NET 5+的发展,这一能力得到了极大加强)。CLR还提供了垃圾回收、异常处理、安全管理等重要服务。
- 基础类库 (BCL): .NET提供了极其丰富的基础类库,包含了文件操作、网络通信、数据结构、字符串处理等各种常用功能的实现。C#开发者可以直接使用这些现成的类和方法,极大地提高了开发效率。
System
是BCL中最核心的命名空间。
所以,当您学习C#时,实际上也在学习如何利用.NET平台提供的资源来构建应用程序。C#代码离开了.NET运行时和类库,就无法编译和运行。
2. C#的基本语法:Hello World与入门
所有编程语言的学习都始于“Hello World”。让我们看看一个最简单的C#程序是什么样的:
“`csharp
using System; // 导入System命名空间
class Program // 定义一个类
{
static void Main(string[] args) // 程序的入口点
{
Console.WriteLine(“Hello, World!”); // 打印到控制台
}
}
“`
解析:
using System;
: 这是一个using
指令,用于导入System
命名空间。System
命名空间包含了许多基础类型和常用功能,比如用于控制台输入的Console
类。导入命名空间后,我们就可以直接使用其中的成员,而无需写完整的名称(如System.Console.WriteLine
)。class Program
: C#是面向对象的语言,代码通常组织在类(Class)中。Program
是一个类的名称。static void Main(string[] args)
: 这是C#程序的入口点。当程序启动时,CLR会查找并执行这个特定的方法。static
: 表示这个方法属于类本身,而不是类的某个具体对象。void
: 表示这个方法不返回任何值。Main
: 方法的名称,必须是这个名字(注意大小写)。(string[] args)
: 这是方法的参数列表。args
是一个字符串数组,用于接收命令行参数(如果程序是通过命令行启动的话)。
Console.WriteLine("Hello, World!");
: 这行代码调用了System
命名空间中Console
类的WriteLine
方法,用于将文本输出到控制台。;
: 每条语句(除了代码块{}
等)都必须以分号结束。{}
: 大括号用于定义代码块,如类体、方法体、循环体等。//
: 双斜杠用于单行注释,/* ... */
用于多行注释。注释是程序员写给其他程序员(或未来的自己)看的说明,编译器会忽略它们。
3. 变量与数据类型:存储数据
变量是用于存储数据值的容器。在使用变量之前,必须先声明它,指定它的类型和名称。
“`csharp
// 声明并初始化一个整数变量
int age = 30;
// 声明一个字符串变量
string name;
name = “Alice”; // 赋值
// 声明一个布尔变量
bool isStudent = true;
“`
C#是强类型语言,这意味着变量一旦声明了某种类型,就只能存储该类型的数据。C#的数据类型可以分为两大类:值类型(Value Types)和引用类型(Reference Types)。理解它们的区别对于编写高效且正确的C#代码至关重要。
3.1 值类型 (Value Types)
值类型变量直接存储它们的值。当您将一个值类型变量赋值给另一个变量时,会复制其值。它们通常存储在栈(Stack)上。
常见的内置值类型包括:
- 整型 (Integer Types):
byte
(8位),short
(16位),int
(32位),long
(64位). 还有无符号版本sbyte
,ushort
,uint
,ulong
. - 浮点型 (Floating-Point Types):
float
(单精度),double
(双精度). - 十进制型 (Decimal Type):
decimal
(用于需要高精度计算的财务货币等场景). - 布尔型 (Boolean Type):
bool
(只能是true
或false
). - 字符型 (Character Type):
char
(存储单个 Unicode 字符). - 结构体 (Structs): 用户自定义的值类型。
- 枚举 (Enums): 定义一组命名的常量。
示例:
csharp
int a = 10;
int b = a; // b gets a copy of a's value
b = 20; // Changing b does not affect a
Console.WriteLine($"a: {a}, b: {b}"); // Output: a: 10, b: 20
3.2 引用类型 (Reference Types)
引用类型变量存储的是对象的内存地址(引用),而不是值本身。当您将一个引用类型变量赋值给另一个变量时,复制的是引用,它们指向同一块内存区域。引用类型对象通常存储在堆(Heap)上。
常见的内置引用类型包括:
- 字符串 (String Type):
string
(不可变的字符序列). - 对象 (Object Type):
object
(所有类型的基类). - 类 (Classes): 用户自定义的引用类型。
- 接口 (Interfaces): 定义一组契约或行为。
- 委托 (Delegates): 引用方法。
- 数组 (Arrays): 存储固定大小的相同类型元素的集合。
示例:
“`csharp
int[] arr1 = { 1, 2, 3 };
int[] arr2 = arr1; // arr2 gets a copy of the reference to the same array
arr2[0] = 99; // Changing arr2 affects arr1 because they point to the same array
Console.WriteLine($”arr1[0]: {arr1[0]}, arr2[0]: {arr2[0]}”); // Output: arr1[0]: 99, arr2[0]: 99
string s1 = “hello”;
string s2 = s1; // For strings, assignment copies the reference.
// However, strings are immutable. Operations that seem to modify a string actually create a new string.
s2 = “world”; // s2 now refers to a new string object. s1 is unchanged.
Console.WriteLine($”s1: {s1}, s2: {s2}”); // Output: s1: hello, s2: world
``
string`是引用类型,但由于其不可变性,它的行为在某些方面类似于值类型,尤其是在简单的赋值操作中。*
*注意:虽然
理解值类型和引用类型的区别对于内存管理、参数传递(值传递 vs 引用传递)、对象比较等方面非常重要。
4. 运算符:执行操作
运算符用于对变量和值执行操作。C#提供了丰富的运算符:
- 算术运算符:
+
,-
,*
,/
,%
(取模). - 赋值运算符:
=
,+=
,-=
,*=
,/=
,%=
. - 比较运算符:
==
(等于),!=
(不等于),>
(大于),<
(小于),>=
(大于等于),<=
(小于等于). - 逻辑运算符:
&&
(逻辑与),||
(逻辑或),!
(逻辑非). - 位运算符:
&
,|
,^
,~
,<<
,>>
. - 其他运算符:
?.
(空条件运算符),??
(空合并运算符),?:
(三元条件运算符),as
(类型转换,如果失败返回null),is
(类型检查),new
(创建对象),typeof
(获取类型信息),sizeof
(获取值类型大小).
示例:
“`csharp
int x = 10;
int y = 5;
Console.WriteLine(x + y); // 15
Console.WriteLine(x > y); // True
Console.WriteLine(x == y); // False
Console.WriteLine((x > 0) && (y < 10)); // True
string message = (age >= 18) ? “Adult” : “Minor”; // 三元运算符
“`
5. 控制流:决定代码的执行路径
控制流语句允许您根据条件执行不同的代码块,或者重复执行某段代码。
5.1 条件语句
if
,else if
,else
:
csharp
int score = 85;
if (score >= 90)
{
Console.WriteLine("优秀");
}
else if (score >= 75)
{
Console.WriteLine("良好");
}
else if (score >= 60)
{
Console.WriteLine("及格");
}
else
{
Console.WriteLine("不及格");
}
switch
:
csharp
char grade = 'B';
switch (grade)
{
case 'A':
Console.WriteLine("棒极了!");
break; // break是必须的,防止“穿透”到下一个case
case 'B':
Console.WriteLine("很好!");
break;
case 'C':
Console.WriteLine("还可以.");
break;
default:
Console.WriteLine("需要努力.");
break;
}
5.2 循环语句
for
循环: 通常用于已知循环次数的情况。
csharp
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"循环次数: {i}");
}
// Output:
// 循环次数: 0
// 循环次数: 1
// 循环次数: 2
// 循环次数: 3
// 循环次数: 4
while
循环: 在循环开始前检查条件,只要条件为真就一直执行。
csharp
int count = 0;
while (count < 3)
{
Console.WriteLine($"计数: {count}");
count++;
}
// Output:
// 计数: 0
// 计数: 1
// 计数: 2
do-while
循环: 先执行一次循环体,然后在循环结束后检查条件。循环体至少会执行一次。
csharp
int num = 5;
do
{
Console.WriteLine($"数字: {num}");
num--;
} while (num > 0);
// Output:
// 数字: 5
// 数字: 4
// 数字: 3
// 数字: 2
// 数字: 1
foreach
循环: 用于遍历数组或集合中的每个元素。
csharp
string[] fruits = { "Apple", "Banana", "Cherry" };
foreach (string fruit in fruits)
{
Console.WriteLine(fruit);
}
// Output:
// Apple
// Banana
// Cherry
- 跳转语句:
break
(跳出当前循环或switch),continue
(跳过当前循环的剩余部分,进入下一次迭代).
6. 方法:代码的组织与重用
方法(Method),在其他语言中也常被称为函数(Function),是将一段逻辑封装起来,赋予它一个名称,可以在程序中多次调用以执行特定任务的代码块。方法提高了代码的模块化、可读性和可重用性。
“`csharp
// 方法定义
// public: 访问修饰符
// static: 表示方法属于类,而不是类的实例
// int: 返回类型 (这里表示返回一个整数)
// Add: 方法名称
// (int a, int b): 参数列表
public static int Add(int a, int b)
{
int sum = a + b;
return sum; // 使用return关键字返回值
}
// 如果方法不返回任何值,返回类型为void
public static void Greet(string name)
{
Console.WriteLine($”你好,{name}!”);
}
// 在Main方法中调用这些方法
static void Main(string[] args)
{
int result = Add(5, 3); // 调用Add方法,传递参数,接收返回值
Console.WriteLine($”5 + 3 = {result}”); // Output: 5 + 3 = 8
Greet("小明"); // 调用Greet方法,传递参数
// Output: 你好,小明!
}
“`
- 方法签名 (Method Signature): 由方法名和参数列表(参数的类型和顺序)组成。方法签名在同一个类中必须是唯一的。
- 方法重载 (Method Overloading): 在同一个类中,可以定义多个同名但参数列表不同的方法。这使得可以用同一个方法名来执行类似但接受不同输入类型的操作。
“`csharp
public static int Add(int a, int b) // Signature: Add(int, int)
{
return a + b;
}
public static double Add(double a, double b) // Signature: Add(double, double)
{
return a + b;
}
public static int Add(int a, int b, int c) // Signature: Add(int, int, int)
{
return a + b + c;
}
“`
编译器会根据调用时提供的参数类型和数量来决定调用哪个具体的方法。
7. 面向对象编程 (OOP):C#的核心
C#是一门典型的面向对象编程语言。理解OOP思想及其在C#中的实现是掌握C#的关键。OOP的四大基本支柱是:封装、继承、多态和抽象。
7.1 类与对象 (Classes and Objects)
- 类 (Class): 类是对象的蓝图或模板,定义了对象的属性(数据,通常用字段或属性表示)和行为(功能,通常用方法表示)。类本身不占用内存,直到创建对象。
- 对象 (Object): 对象是类的具体实例。创建对象的过程称为实例化。每个对象都有自己的属性值,并可以调用类中定义的方法。
“`csharp
// 定义一个类
public class Dog
{
// 属性 (Properties) – 推荐使用属性而非直接使用字段
public string Name { get; set; } // Auto-implemented property
public int Age { get; set; }
// 字段 (Fields) - 私有字段通常与公共属性配合使用实现封装
private string breed;
// 构造函数 (Constructor) - 用于初始化新创建的对象
public Dog(string name, int age, string breed)
{
Name = name;
Age = age;
this.breed = breed; // 使用this区分参数名和字段名
Console.WriteLine($"一只名叫 {Name} 的 {breed} 狗被创建了!");
}
// 方法 (Methods) - 定义对象的行为
public void Bark()
{
Console.WriteLine($"{Name} 说: 汪汪!");
}
public void DisplayInfo()
{
Console.WriteLine($"名字: {Name}, 年龄: {Age}, 品种: {breed}");
}
}
// 在Main方法中创建并使用对象
static void Main(string[] args)
{
// 创建 Dog 类的对象 (实例化)
Dog myDog = new Dog(“旺财”, 3, “金毛”);
// 访问对象的属性
Console.WriteLine($"我的狗叫 {myDog.Name}");
// 调用对象的方法
myDog.Bark();
myDog.DisplayInfo();
// Output:
// 一只名叫 旺财 的 金毛 狗被创建了!
// 我的狗叫 旺财
// 旺财 说: 汪汪!
// 名字: 旺财, 年龄: 3, 品种: 金毛
}
“`
- 字段 (Fields): 类中用于存储数据的变量。通常声明为
private
以实现封装。 - 属性 (Properties): 提供一种灵活的机制来读取、写入或计算私有字段的值。它们提供了一种受控的方式来访问对象的内部状态,可以在
get
和set
访问器中包含逻辑。C#提供了自动实现的属性(如public string Name { get; set; }
)来简化常见场景。 - 构造函数 (Constructors): 一个特殊的方法,与类同名,没有返回类型。它在创建类的新实例时自动调用,用于初始化对象的属性或执行必要的设置。一个类可以有多个构造函数(重载)。
new
关键字: 用于创建类的新实例(对象)。
7.2 封装 (Encapsulation)
封装是将数据(字段)和操作数据的方法(方法)捆绑在一起,形成一个独立的单元(类)。同时,通过访问修饰符(public
, private
, protected
, internal
等),控制外部对类成员的访问权限,隐藏内部实现细节,只暴露必要的接口。
public
: 成员可以从任何地方访问。private
: 成员只能在类的内部访问。protected
: 成员可以在类的内部及其派生类中访问。internal
: 成员可以在同一个程序集(Assembly,通常是一个.dll
或.exe
文件)内访问。
封装的好处:
* 数据保护: 防止外部代码随意修改对象的内部状态。
* 代码模块化: 使得类成为独立的单元,易于管理和维护。
* 隐藏实现: 可以在不影响外部代码的情况下修改类的内部实现。
在上面的Dog
类示例中,private string breed;
字段就是一个封装的例子,外部无法直接访问,只能通过类内部的方法(如DisplayInfo
)来获取其信息。public string Name { get; set; }
虽然看起来可以直接访问,但实际上是通过编译器生成的get
和set
访问器来实现的,这是一种更高级的封装形式。
7.3 继承 (Inheritance)
继承允许一个类(派生类或子类)继承另一个类(基类或父类)的属性和方法。派生类可以重用基类的代码,并可以添加新的成员或修改继承来的成员的行为(通过多态)。继承表示一种“is-a”关系(例如,“电动车是一种汽车”)。
“`csharp
// 基类
public class Vehicle
{
public string Model { get; set; }
public int Year { get; set; }
public Vehicle(string model, int year)
{
Model = model;
Year = year;
Console.WriteLine($"创建了 {Year} 年款的 {Model}");
}
public void StartEngine()
{
Console.WriteLine("引擎启动.");
}
// 标记为 virtual,允许派生类重写 (override)
public virtual void Drive()
{
Console.WriteLine("车辆正在行驶.");
}
}
// 派生类,继承自 Vehicle
public class ElectricCar : Vehicle
{
public int BatteryCapacity { get; set; }
// 派生类的构造函数必须调用基类的构造函数
public ElectricCar(string model, int year, int batteryCapacity)
: base(model, year) // 调用基类构造函数
{
BatteryCapacity = batteryCapacity;
Console.WriteLine($"这是电动汽车,电池容量 {BatteryCapacity} kWh.");
}
// 使用 override 关键字重写基类的虚方法
public override void Drive()
{
Console.WriteLine("电动汽车正在静音行驶.");
}
// 添加 ElectricCar 特有的方法
public void Charge()
{
Console.WriteLine("电动汽车正在充电.");
}
}
// 在Main方法中使用继承
static void Main(string[] args)
{
ElectricCar myCar = new ElectricCar(“特斯拉 Model 3”, 2023, 75);
myCar.StartEngine(); // 调用继承自 Vehicle 的方法
myCar.Drive(); // 调用 ElectricCar 中重写的方法 (多态体现)
myCar.Charge(); // 调用 ElectricCar 特有的方法
// Output:
// 创建了 2023 年款的 特斯拉 Model 3
// 这是电动汽车,电池容量 75 kWh.
// 引擎启动.
// 电动汽车正在静音行驶.
// 电动汽车正在充电.
}
``
:
* **符号:** 用于指定一个类继承自另一个类。
base
* **关键字:** 用于访问基类的成员,最常见的是在派生类构造函数中调用基类构造函数。
virtual
* **关键字:** 用于标记基类方法,表示该方法可以在派生类中被重写。
override` 关键字:** 用于在派生类中标记重写基类方法。
* **
继承的好处:
* 代码重用: 避免重复编写相同的代码。
* 易于维护和扩展: 修改基类可以影响所有派生类,添加新功能时可以在派生类中实现。
7.4 多态 (Polymorphism)
多态意味着“多种形态”。在OOP中,多态性允许您使用基类类型的变量来引用派生类类型的对象,并在运行时根据对象的实际类型调用相应的方法。这通常通过虚方法(virtual
/ override
)或接口实现。
“`csharp
static void Main(string[] args)
{
Vehicle basicVehicle = new Vehicle(“普通汽车”, 2020);
Vehicle electricVehicle = new ElectricCar(“比亚迪汉”, 2024, 80); // 使用基类变量引用派生类对象
basicVehicle.Drive(); // 调用 Vehicle 的 Drive 方法: "车辆正在行驶."
electricVehicle.Drive(); // 调用 ElectricCar 的 Drive 方法: "电动汽车正在静音行驶." (运行时多态)
// Output:
// 创建了 2020 年款的 普通汽车
// 创建了 2024 年款的 比亚迪汉
// 这是电动汽车,电池容量 80 kWh.
// 车辆正在行驶.
// 电动汽车正在静音行驶.
}
“`
尽管electricVehicle
变量的类型是Vehicle
(基类),但在调用Drive()
方法时,实际执行的是它所引用的ElectricCar
对象中重写的Drive()
方法。这就是运行时多态性。
多态的好处:
* 灵活性: 可以编写处理不同类型对象(只要它们继承自同一个基类或实现了同一个接口)的通用代码。
* 可扩展性: 添加新的派生类而无需修改处理基类类型变量的现有代码。
7.5 抽象 (Abstraction)
抽象是指隐藏复杂的实现细节,只向外部暴露必要的特征或功能。在C#中,抽象主要通过抽象类(Abstract Classes)和接口(Interfaces)来实现。
- 抽象类 (Abstract Classes):
- 使用
abstract
关键字声明。 - 不能直接实例化(不能用
new
创建对象)。 - 可以包含抽象成员(如抽象方法),这些成员只有声明没有实现,必须由派生类提供具体实现。
- 可以包含非抽象成员(普通字段、属性、方法、构造函数)。
- 一个类只能继承一个抽象类。
- 抽象类通常用于定义一类事物的共同特征和行为的骨架。
- 使用
“`csharp
// 抽象类
public abstract class Shape
{
public string Name { get; set; }
public Shape(string name)
{
Name = name;
}
// 抽象方法 (没有方法体)
public abstract double GetArea();
// 非抽象方法
public void DisplayName()
{
Console.WriteLine($"形状名称: {Name}");
}
}
// 派生类,必须实现所有抽象方法
public class Circle : Shape
{
public double Radius { get; set; }
public Circle(string name, double radius) : base(name)
{
Radius = radius;
}
// 实现抽象方法 GetArea
public override double GetArea()
{
return Math.PI * Radius * Radius;
}
}
// 在Main方法中使用抽象类和派生类
static void Main(string[] args)
{
// 不能直接实例化抽象类
// Shape myShape = new Shape(“Generic”); // Error
Circle circle = new Circle("我的圆", 5);
circle.DisplayName(); // 调用继承的非抽象方法
Console.WriteLine($"面积: {circle.GetArea()}"); // 调用实现的抽象方法
// 可以使用抽象类引用派生类对象 (多态)
Shape anotherCircle = new Circle("另一个圆", 10);
Console.WriteLine($"另一个圆的面积: {anotherCircle.GetArea()}"); // 多态调用实现的GetArea
}
“`
- 接口 (Interfaces):
- 使用
interface
关键字声明。 - 只包含成员的声明(方法、属性、事件、索引器),没有实现。
- 接口成员默认是公共的(
public
),且不能包含访问修饰符、字段、构造函数。 - 类或结构体可以实现(
implement
)一个或多个接口。实现接口的类或结构体必须提供接口中所有成员的具体实现。 - 接口表示一种“can-do”能力或契约(例如,“会飞的物体”)。
- 使用
“`csharp
// 接口
public interface IFlyable
{
void Fly(); // 接口方法声明,没有方法体
int Speed { get; } // 接口属性声明
}
// 实现接口的类
public class Bird : IFlyable
{
public int Speed { get; } = 50;
public void Fly() // 实现接口方法
{
Console.WriteLine("鸟儿在飞翔!");
}
}
public class Airplane : IFlyable
{
public int Speed { get; } = 800;
public void Fly() // 实现接口方法
{
Console.WriteLine("飞机在空中翱翔!");
}
}
// 在Main方法中使用接口
static void Main(string[] args)
{
// 使用接口类型变量引用实现接口的对象 (多态)
IFlyable creature1 = new Bird();
IFlyable creature2 = new Airplane();
creature1.Fly(); // Output: 鸟儿在飞翔!
Console.WriteLine($"速度: {creature1.Speed}"); // Output: 速度: 50
creature2.Fly(); // Output: 飞机在空中翱翔!
Console.WriteLine($"速度: {creature2.Speed}"); // Output: 速度: 800
}
“`
抽象的好处:
* 降低复杂性: 隐藏不相关的细节,让代码更易于理解和使用。
* 定义契约: 接口强制实现者提供特定的功能,提高了代码的可替换性和模块化。
8. 命名空间:代码的组织单元
命名空间(Namespace)是用于组织代码和避免命名冲突的一种机制。可以将相关的类、结构体、接口、枚举、委托等放入同一个命名空间中。
“`csharp
// 定义命名空间
namespace MyApplication.Data
{
public class Customer
{
// … Customer 类的定义 …
}
}
namespace MyApplication.BusinessLogic
{
public class OrderProcessor
{
// … OrderProcessor 类的定义 …
}
}
// 在其他地方使用命名空间中的类
using MyApplication.Data; // 导入命名空间
using MyApplication.BusinessLogic;
static void Main(string[] args)
{
Customer customer = new Customer(); // 可以直接使用 Customer
OrderProcessor processor = new OrderProcessor(); // 可以直接使用 OrderProcessor
// 如果没有 using 指令,需要使用完全限定名
// MyApplication.Data.Customer customer = new MyApplication.Data.Customer();
}
“`
using
指令允许您在当前文件中直接使用指定命名空间中的类型,而无需写完整的命名空间前缀。这是组织大型项目时非常重要的概念。
9. 异常处理:优雅地应对错误
异常(Exception)是程序运行时发生的错误或意外情况,会中断程序的正常流程。C#提供了结构化的异常处理机制,允许您捕获并处理这些异常,从而防止程序崩溃。
核心的异常处理关键字是 try
, catch
, finally
。
“`csharp
static void Main(string[] args)
{
try
{
// 可能会抛出异常的代码块
int a = 10;
int b = 0;
int result = a / b; // 这行代码会抛出 DivideByZeroException
Console.WriteLine($”结果: {result}”); // 这行代码将不会被执行
}
catch (DivideByZeroException ex) // 捕获特定类型的异常
{
// 发生 DivideByZeroException 时执行的代码
Console.WriteLine($”捕获到除以零异常: {ex.Message}”);
}
catch (Exception ex) // 捕获其他所有类型的异常 (更通用的异常类型放在后面)
{
// 捕获任何其他类型的异常
Console.WriteLine($”捕获到一般异常: {ex.Message}”);
}
finally
{
// 无论是否发生异常,finally 块中的代码总会执行
Console.WriteLine(“异常处理块结束.”);
}
Console.WriteLine("程序继续执行.");
}
“`
try
块:包含可能抛出异常的代码。catch
块:用于捕获并处理特定类型的异常。可以有多个catch
块来处理不同类型的异常。更具体的异常类型应该放在前面。finally
块:可选,包含无论是否发生异常都必须执行的代码,常用于资源清理(如关闭文件流、数据库连接)。
通过异常处理,您可以使程序更加健壮,能够优雅地从错误中恢复,而不是直接崩溃。
10. 集合:管理数据组
在编程中,经常需要处理一组相关的数据。C#提供了多种集合类型来存储和管理这些数据。最基础和常用的集合是数组和列表。
- 数组 (Arrays):
- 存储固定大小的相同类型元素的集合。
- 一旦创建,大小不能改变。
- 元素通过索引访问,索引从 0 开始。
“`csharp
// 声明并初始化一个整数数组
int[] numbers = new int[5]; // 创建一个包含5个整数的数组
numbers[0] = 10;
numbers[1] = 20;
// …
// 声明并初始化一个字符串数组
string[] colors = { “Red”, “Green”, “Blue” };
Console.WriteLine($”数组长度: {colors.Length}”); // Output: 数组长度: 3
Console.WriteLine($”第一个颜色: {colors[0]}”); // Output: 第一个颜色: Red
// 遍历数组 (可以使用 for 或 foreach)
for (int i = 0; i < numbers.Length; i++)
{
Console.WriteLine($”numbers[{i}] = {numbers[i]}”);
}
foreach (string color in colors)
{
Console.WriteLine(color);
}
“`
- 列表 (
List<T>
):- 存储可变大小的相同类型元素的集合。
- 属于泛型集合 (
<T>
表示元素类型),在使用时需要指定存储的数据类型(例如List<int>
,List<string>
,List<Dog>
). - 提供了方便的方法用于添加、删除、查找元素等。
- 位于
System.Collections.Generic
命名空间中。
“`csharp
using System.Collections.Generic; // 需要导入泛型集合命名空间
// 创建一个整数列表
List
// 添加元素
scores.Add(95);
scores.Add(88);
scores.Add(72);
// 访问元素 (通过索引)
Console.WriteLine($”第二个分数: {scores[1]}”); // Output: 第二个分数: 88
// 列表长度
Console.WriteLine($”列表元素个数: {scores.Count}”); // Output: 列表元素个数: 3
// 遍历列表 (通常使用 foreach)
foreach (int score in scores)
{
Console.WriteLine(score);
}
// 移除元素
scores.Remove(88); // 移除值为 88 的元素
scores.RemoveAt(0); // 移除索引为 0 的元素 (现在的 95)
Console.WriteLine(“移除后:”);
foreach (int score in scores)
{
Console.WriteLine(score); // Output: 72
}
“`
除了 List<T>
,System.Collections.Generic
命名空间还提供了许多其他有用的泛型集合,如 Dictionary<TKey, TValue>
(键值对集合), HashSet<T>
(不包含重复元素的集合), Queue<T>
(队列), Stack<T>
(堆栈) 等。掌握如何使用这些集合是处理复杂数据的基础。
总结与展望
恭喜您!通过阅读本文,您已经快速了解了C#语言的核心概念:
- C#与.NET平台: 理解了它们之间的紧密关系以及CLR和BCL的作用。
- 基本语法: 掌握了程序的结构、变量、数据类型(尤其是值类型和引用类型的区别)、运算符。
- 控制流: 学会了如何使用条件语句(
if
,switch
)和循环语句(for
,while
,do-while
,foreach
)来控制程序执行流程。 - 方法: 理解了如何组织和重用代码块。
- 面向对象编程 (OOP): 深入理解了类与对象、封装、继承、多态和抽象这四大核心概念,以及它们在C#中的实现方式(类、属性、构造函数、访问修饰符、继承、虚方法、抽象类、接口)。
- 命名空间: 理解了如何组织代码和避免冲突。
- 异常处理: 知道了如何使用
try-catch-finally
来处理运行时错误。 - 集合: 掌握了数组和列表等基础数据结构的用法。
这些核心概念是构建任何C#应用程序的基础。然而,C#是一个功能极其丰富的语言,还有许多更高级的特性等待您去探索,例如:
- 泛型 (Generics): 编写类型安全且可重用的代码(例如
List<T>
)。 - 委托和事件 (Delegates and Events): 实现回调和事件驱动编程。
- LINQ (Language Integrated Query): 强大的数据查询语言。
- 异步编程 (Async/Await): 编写高效的非阻塞代码。
- Lambda 表达式和匿名方法: 简洁地定义小型函数。
- 扩展方法: 在不修改现有类的情况下为其添加方法。
- Nullabe 类型和 Null 安全: 更好地处理可能为 null 的值。
下一步该做什么?
掌握了核心概念后,最重要的是实践!
- 安装开发环境: 下载并安装 Visual Studio 或 Visual Studio Code,它们提供了强大的代码编辑、编译和调试功能。
- 动手编写代码: 从简单的控制台应用程序开始,尝试实现本文中的示例代码,并在此基础上进行修改和扩展。
- 解决实际问题: 尝试用C#解决一些小问题,比如计算器、通讯录管理、简单的文件读写等。
- 学习进阶主题: 阅读官方文档、教程或书籍,逐步学习C#和.NET更高级的特性和框架(如ASP.NET用于Web开发,WPF用于桌面应用,Unity用于游戏开发)。
- 参与社区: 加入C#开发者社区,与其他开发者交流学习经验。
C#是一个充满活力且不断发展的语言。通过持续学习和实践,您将能够驾驭这门强大的工具,构建出令人印象深刻的应用程序。祝您在C#的学习旅程中一切顺利!