👏🏻 你好!欢迎访问IT教程网,0门教程,教程全部原创,计算机教程大全,全免费!

🔥 新增教程

《黑神话 悟空》游戏开发教程,共40节,完全免费,点击学习

《AI副业教程》,完全原创教程,点击学习

13 控制结构之条件语句

在上一篇中,我们讨论了数据类型和变量,特别是常量与字面量的使用。这一篇我们将深入探讨控制结构中的条件语句,条件语句是C#中非常重要的一部分,它允许程序根据不同的条件采取不同的行动。

什么是条件语句?

条件语句是用于控制程序执行流程的逻辑结构,它能够根据给定的条件判断结果执行不同的代码块。最常用的条件语句有 if 语句、switch 语句和 ternary 运算符(条件运算符)。

1. if 语句

if 语句用于执行一个条件为真的代码块。如果条件为假,则可以使用 else 子句来执行另一段代码。

语法结构

1
2
3
4
5
6
7
8
if (condition)
{
// 当条件为真执行的代码块
}
else
{
// 当条件为假执行的代码块
}

示例代码

以下是一个使用 if 语句的简单示例,判断一个数字是正数、负数还是零。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int number = -5;

if (number > 0)
{
Console.WriteLine("这个数字是正数。");
}
else if (number < 0)
{
Console.WriteLine("这个数字是负数。");
}
else
{
Console.WriteLine("这个数字是零。");
}

2. switch 语句

switch 语句提供了一种更加整洁的方式来处理多个条件,特别是当你需要基于某个变量的不同值执行不同的代码块时。

语法结构

1
2
3
4
5
6
7
8
9
10
11
12
switch (expression)
{
case value1:
// 当expression等于value1时执行的代码块
break;
case value2:
// 当expression等于value2时执行的代码块
break;
default:
// 当expression不匹配任何case时执行的代码块
break;
}

示例代码

以下是一个使用 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
int day = 3;

switch (day)
{
case 1:
Console.WriteLine("今天是星期一。");
break;
case 2:
Console.WriteLine("今天是星期二。");
break;
case 3:
Console.WriteLine("今天是星期三。");
break;
case 4:
Console.WriteLine("今天是星期四。");
break;
case 5:
Console.WriteLine("今天是星期五。");
break;
case 6:
Console.WriteLine("今天是星期六。");
break;
case 7:
Console.WriteLine("今天是星期日。");
break;
default:
Console.WriteLine("无效的星期数字。");
break;
}

3. 条件运算符

C#中的条件运算符(也称为三元运算符)允许你在一行代码中进行条件判断。它的语法如下:

1
result = condition ? value_if_true : value_if_false;

示例代码

以下示例展示了如何使用条件运算符来判断一个年龄是否成年:

1
2
3
int age = 20;
string result = (age >= 18) ? "成年" : "未成年";
Console.WriteLine($"这个人是{result}。");

总结

在这一篇中,我们详细探讨了C#中的条件语句,包括 if 语句、switch 语句和条件运算符。条件语句是构建复杂逻辑的基础,有助于我们的代码根据实际情况做出不同的反应。

在下一篇教程中,我们将讨论控制结构中的循环语句,它们允许我们执行重复的操作,提高代码的效率和灵活性。请继续关注!

分享转发

14 控制结构之循环语句

在前一篇中,我们讨论了 C# 中的控制结构之一——条件语句。条件语句允许我们根据特定条件的真假来决定程序的执行流程。而在本篇中,我们将探讨控制结构的另一个重要部分:循环语句。

循环语句使我们可以重复执行某段代码,直到满足某个条件为止。C# 中主要有三种循环语句:for 循环、while 循环和 do...while 循环。接下来,我们将逐一介绍这三种循环语句,并通过具体的示例来帮助理解。

1. for 循环

for 循环是最常用的循环结构之一,通常用于已知循环次数的情况。其语法如下:

1
2
3
4
for (初始化; 条件; 迭代)
{
// 循环体
}

示例

假设我们希望打印出从 1 到 5 的数字,可以这样写:

1
2
3
4
for (int i = 1; i <= 5; i++)
{
Console.WriteLine(i);
}

在这个例子中:

  • 初始化int i = 1,我们声明并初始化循环变量 i
  • 条件i <= 5,只要 i 小于或等于 5,循环将继续执行。
  • 迭代i++,每次循环结束时 i 的值增加 1。

运行上面的代码,将输出:

1
2
3
4
5
1
2
3
4
5

2. while 循环

while 循环用于在条件为真时反复执行某段代码。其语法如下:

1
2
3
4
while (条件)
{
// 循环体
}

示例

我们来看一个例子,需要打印从 1 开始的数字,直到数字大于 5 为止:

1
2
3
4
5
6
int i = 1;
while (i <= 5)
{
Console.WriteLine(i);
i++;
}

这里:

  • 我们初始化变量 i 为 1。
  • while 循环将持续执行,只要 i 小于等于 5。
  • 在每次循环中,打印 i 的值并将其递增。

执行上述代码的输出为:

1
2
3
4
5
1
2
3
4
5

3. do...while 循环

do...while 循环与 while 循环类似,不同之处在于 do...while 循环至少会执行一次循环体。其语法如下:

1
2
3
4
do
{
// 循环体
} while (条件);

示例

以下示例展示了我们如何使用 do...while 循环,确保打印数字 1 至 5,即使初始条件不满足:

1
2
3
4
5
6
int i = 1;
do
{
Console.WriteLine(i);
i++;
} while (i <= 5);

尽管 i 的初始值为 1,该代码仍然会输出:

1
2
3
4
5
1
2
3
4
5

即使 i 初始值设置得不正确,do...while 依然保证循环体至少执行一次。

总结

在这一节中,我们学习了 C# 中的三种主要循环结构:forwhiledo...while。通过示例代码,我们能够更好地理解如何在实际应用中使用这些循环。为了提高编程的灵活性和效率,熟练掌握循环语句是非常重要的。

在下一篇文章中,我们将继续讨论控制结构中的 switch 语句,这是另一种重要的控制流结构,用于处理多重分支的场景。希望本篇文章能为你在 C# 中的学习打下坚实的基础!

分享转发

15 控制结构之switch语句

在上一篇的教程中,我们介绍了循环语句,了解了如何使用 forwhiledo...while 来控制程序的执行流。在这一篇中,我们将专注于另一种重要的控制结构——switch 语句。switch 语句通常用于根据一个变量的不同值来执行不同的代码块。

为什么使用switch语句

在某些情况下,使用多个 if...else 语句来处理多个条件判断会使代码变得复杂且难以维护。switch 语句提供了一种更简洁的方式来处理多个可能的情况,使得代码更易读、更易维护。

switch语句的基本结构

switch 语句的基本结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
switch (变量)
{
case1:
// 当变量等于值1时执行的代码
break; // 结束当前case,防止落入下一case
case2:
// 当变量等于值2时执行的代码
break;
// 可以有任意数量的case
default:
// 如果没有case匹配时执行的代码
break;
}

关键字解释

  • switch: 声明一个 switch 语句。
  • case: 定义一个条件分支,每个 case 下的代码块在满足条件时执行。
  • break: 结束当前的 case,防止继续执行后续的 case 代码。如果省略 break 语句,程序会继续执行下一个 case 的代码,这称为“fall-through”。
  • default: 当所有的 case 都不匹配时执行的代码块,类似于 if...else 语句中的 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
33
34
35
36
37
38
using System;

class Program
{
static void Main(string[] args)
{
Console.WriteLine("请输入一个数字(1到7)表示星期几:");
int day = Convert.ToInt32(Console.ReadLine());

switch (day)
{
case 1:
Console.WriteLine("星期一");
break;
case 2:
Console.WriteLine("星期二");
break;
case 3:
Console.WriteLine("星期三");
break;
case 4:
Console.WriteLine("星期四");
break;
case 5:
Console.WriteLine("星期五");
break;
case 6:
Console.WriteLine("星期六");
break;
case 7:
Console.WriteLine("星期日");
break;
default:
Console.WriteLine("无效的输入,请输入1到7的数字。");
break;
}
}
}

在这个示例中,我们提示用户输入一个数字,表示星期几。根据用户的输入,程序会输出对应的星期几名称。如果输入不在 1 到 7 的范围内,则输出无效的提示。

小结

在这一篇中,我们学习了 switch 语句的基本用法和结构,并通过示例代码展示了如何使用 switch 来实现简单的条件控制。这种语句特别适合用于多个可能值的选择情境,使代码更加清晰。在下一篇中,我们将进一步探讨函数和方法的定义,帮助你整理和复用代码,让编程变得更高效!

希望对你理解 switch 语句有所帮助,接下来我们会在函数的使用上继续深入探讨。

分享转发

16 函数和方法之定义函数

在上一篇教程中,我们学习了控制结构中的 switch 语句,可以选择不同的执行路径来处理不同的情况。那么在编程中,我们常常会将一些可重复使用的代码块抽象为函数方法。在这篇教程中,我们将深入探讨如何定义函数,以及它们在 C# 编程中的作用。

什么是函数和方法?

在 C# 中,函数方法基本上可以看作是同义词,它们都是用于实现某个特定任务的代码块。主要区别在于,函数通常是指独立的代码块,而 方法 是属于某个对象或类的代码块。

函数的结构

一个函数的定义通常包括以下几个部分:

  • 返回类型:函数执行后返回的值类型,如果不返回值,使用 void
  • 函数名:用于调用函数的名称。
  • 参数列表:输入参数的类型及名称,多个参数用逗号分隔。
  • 函数体:包含具体逻辑的代码。

其基本结构如下所示:

1
2
3
返回类型 函数名(参数类型 参数名) {
// 函数体代码
}

示例:定义一个简单的函数

下面是一个简单示例,定义一个名为 Add 的函数,用于返回两个整数的和:

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

在这个示例中:

  • int 是返回类型,表示这个函数会返回一个整数。
  • Add 是函数名。
  • (int a, int b) 是参数列表,两个整数 ab
  • return a + b; 是函数体,计算并返回 ab 的和。

调用函数

为了使用我们定义的 Add 函数,我们需要调用它。调用函数可以在程序中的任何地方进行,只要函数在调用它的位置是可见的。

1
2
int result = Add(5, 7);
Console.WriteLine(result); // 输出 12

在这里,我们将 57 作为参数传递给 Add 函数,返回值存储在 result 变量中,最终通过 Console.WriteLine 输出该结果。

函数中的多个参数

函数不仅可以有多个参数,也可以有不同的数据类型。例如:

1
2
3
public double CalculateArea(double length, double width) {
return length * width;
}

在这个示例中,我们定义了一个 CalculateArea 函数来计算矩形的面积,接受两个 double 类型的参数 lengthwidth

调用时的示例:

1
2
double area = CalculateArea(3.5, 4.2);
Console.WriteLine(area); // 输出 14.7

总结

在本节中,我们学习了如何在 C# 中定义函数和方法。我们讨论了函数的基本结构,包括返回类型、函数名、参数列表和函数体; 通过简单的例子展示了如何定义和调用函数。在接下来的教程中,我们将讨论函数和方法的参数与返回值,这对我们使用函数进行更复杂的操作非常重要。请继续关注!

分享转发

17 函数和方法之参数与返回值

在上一篇教程中,我们学习了如何定义一个函数和方法。在本篇中,我们将深入探讨如何为这些函数和方法设置参数,以及它们如何返回值。了解参数与返回值的概念是掌握C#编程的关键步骤之一。

一、函数与方法的参数

在C#中,函数和方法可以接收输入,这些输入称为参数。通过参数,我们可以将所需的数据传递给函数,以便在函数内部进行处理。

1. 参数的定义

在定义函数时,可以在函数声明中指定参数。参数的定义包括参数类型和参数名称,多个参数用逗号分隔。下面是一个简单的示例:

1
2
3
4
public int Add(int a, int b)
{
return a + b;
}

这里,Add方法接受两个参数ab,它们都是int类型。

2. 方法的调用

在调用函数或方法时,必须传入正确数量和类型的参数。例如:

1
int sum = Add(5, 10); // sum的值为15

3. 参数的类型

参数可以是基本数据类型,如 intfloatstring 等,也可以是复杂数据类型,如对象、数组或自定义类型。

例如,一个接受string类型参数的方法:

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

调用这个方法:

1
Greet("Alice"); // 输出:Hello, Alice!

二、参数的传递方式

C#支持不同的参数传递方式,主要有以下两种:

1. 值传递

在值传递中,函数接收的是参数的副本,对副本的修改不会影响原始数据。默认情况下,C#使用值传递。

1
2
3
4
5
6
7
8
public void ModifyValue(int number)
{
number += 10; // 仅修改number的副本
}

int original = 5;
ModifyValue(original);
Console.WriteLine(original); // 输出:5,original未被修改

2. 引用传递

通过refout关键字,可以实现引用传递。在这种情况下,当参数被修改时,原始数据也会受到影响。

1
2
3
4
5
6
7
8
public void ModifyReference(ref int number)
{
number += 10; // 修改原始数据
}

int original = 5;
ModifyReference(ref original);
Console.WriteLine(original); // 输出:15,original已被修改

三、返回值

函数和方法通常会返回一个值,返回值的类型在函数定义时指定。如果没有返回值,可以使用void关键字。

1. 返回值类型

例如,一个返回int类型的函数:

1
2
3
4
public int Multiply(int x, int y)
{
return x * y;
}

调用函数并获取返回值:

1
int product = Multiply(4, 5); // product的值为20

2. 返回值的使用

返回值可以直接用于计算、输出或赋值给变量。例如:

1
Console.WriteLine(Multiply(3, 2)); // 输出:6

四、案例综合

结合以上知识,我们可以编写一个完整的示例。在这个示例中,我们将创建一个简单的计算器,具备加法和乘法功能。

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 Calculator
{
public int Add(int a, int b)
{
return a + b;
}

public int Multiply(int x, int y)
{
return x * y;
}
}

class Program
{
static void Main(string[] args)
{
Calculator calc = new Calculator();

int sum = calc.Add(10, 20);
int product = calc.Multiply(5, 6);

Console.WriteLine("Sum: " + sum); // 输出:Sum: 30
Console.WriteLine("Product: " + product); // 输出:Product: 30
}
}

在这个示例中,我们定义了一个Calculator类,包含AddMultiply方法。这些方法接收参数并返回结果,然后在Main方法中调用这些方法。

总结

通过本篇教程,我们学习了C#函数和方法的参数与返回值的基本概念。理解这些内容将使你能够编写更加灵活和强大的代码。在下一篇教程中,我们将探讨局部变量与全局变量的概念,进一步加深对C#编程的理解。

分享转发

18 函数和方法之局部变量与全局变量

在上一篇中,我们讨论了“函数和方法之参数与返回值”,了解了如何将数据传递给函数,以及如何从函数中返回数据。今天,我们将重点讨论局部变量和全局变量的概念,以及它们在函数和方法中的应用。

局部变量

局部变量是在函数或方法内部定义的变量。它们的作用域仅限于定义它们的代码块内。在代码块之外,它们是不可见的,无法访问。在执行函数时,局部变量会被创建,函数执行完后,局部变量会被销毁。

例子

让我们通过一个简单的示例来了解局部变量的使用。

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

class Program
{
static void Main()
{
int result = Add(5, 10);
Console.WriteLine("结果是: " + result);
}

static int Add(int a, int b)
{
int sum = a + b; // `sum` 是局部变量
return sum; // 返回局部变量
}
}

在这个例子中,Add 方法中定义的 sum 变量是一个局部变量。我们在 Add 方法内部使用它来保存两个参数 ab 的和。你无法在 Add 方法外部访问 sum 变量。

全局变量

全局变量是定义在类的作用域内,但不在任何方法或代码块内部的变量。它们在整个类中都是可见的,且可以被类中的任何方法访问。全局变量通常用于保存类的状态或需要共享的数据。

例子

让我们看看全局变量是如何工作的。

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

class Program
{
static int globalCounter = 0; // `globalCounter` 是全局变量

static void Main()
{
IncrementCounter();
IncrementCounter();
Console.WriteLine("全局计数器的值是: " + globalCounter);
}

static void IncrementCounter()
{
globalCounter++; // 操作全局变量
}
}

在这个例子中,globalCounter 是一个全局变量,它在 Main 方法和 IncrementCounter 方法中都可以访问。不过需要注意的是,过多使用全局变量可能会导致代码难以理解和维护,因此应谨慎使用。

局部变量与全局变量的比较

特性 局部变量 全局变量
作用域 函数/方法内部 整个类
生命周期 仅在方法执行期间有效 程序运行期间有效
访问性 仅在定义它的代码块内可见 可以在类的任意方法中访问
适用场景 常用于临时存储计算中间结果 常用于保存类的状态或共享数据

总结

在本篇中,我们学习了局部变量和全局变量的概念以及它们在 C# 中的基本用法。局部变量提供了一个清晰的方式来在函数中处理数据,而全局变量则用于在类内部共享状态。在下一篇中,我们将进入“面向对象编程基础之类的定义”,继续探索 C# 的强大功能。

感谢您的阅读,期待下次见面!

分享转发

19 C# 面向对象编程基础

在这一篇中,我们将探讨 C# 中面向对象编程(OOP)的基础概念。面向对象编程是一种编程范式,它使用“对象”来表示和管理数据。对象是程序中的实体,它封装了数据和对数据的操作。我们将讨论一些重要的OOP概念,包括类、对象、封装、继承和多态。

类与对象

在 C# 中,是对象的蓝图或模板。它定义了对象的属性和方法。对象则是类的实例。你可以将类类比于一种描述,具体的对象则是这种描述的实现。

示例

以下是一个简单的 C# 类和对象的例子:

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 Car
{
public string Make { get; set; }
public string Model { get; set; }
public int Year { get; set; }

public void DisplayInfo()
{
Console.WriteLine($"Car Info: {Year} {Make} {Model}");
}
}

class Program
{
static void Main(string[] args)
{
Car myCar = new Car();
myCar.Make = "Toyota";
myCar.Model = "Camry";
myCar.Year = 2020;

myCar.DisplayInfo(); // 输出: Car Info: 2020 Toyota Camry
}
}

在这个例子中,我们定义了一个 Car 类,它包含 MakeModelYear 等属性,以及一个方法 DisplayInfo,用于显示汽车的信息。然后我们在 Main 方法中实例化了一个 Car 对象。

封装

封装是面向对象编程的一个重要特性。它指的是将数据(属性)和处理数据的代码(方法)放在一起,并对外界隐藏内部实现细节。封装有助于提高代码的安全性和可维护性。

在 C# 中,我们可以通过访问修饰符(如 publicprivateprotected 等)来控制类的成员能被外部访问的程度:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class BankAccount
{
private decimal balance; // 私有字段

public void Deposit(decimal amount)
{
if (amount > 0)
balance += amount;
}

public decimal GetBalance()
{
return balance;
}
}

在上面的例子中,balance 字段是私有的,外部无法直接访问它。用户必须使用 DepositGetBalance 方法来与 balance 进行交互(即进行存款和查询余额)。

继承

继承允许一个类(子类)从另一个类(父类)获取属性和方法,以实现代码的重用。C# 支持单继承,也就是说一个类只能继承自一个直接父类,但可以实现多个接口。

示例

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
public class Vehicle
{
public void Start()
{
Console.WriteLine("Vehicle is starting");
}
}

public class Bicycle : Vehicle // Bicycle 继承自 Vehicle
{
public void RingBell()
{
Console.WriteLine("Ring ring!");
}
}

class Program
{
static void Main(string[] args)
{
Bicycle myBicycle = new Bicycle();
myBicycle.Start(); // 输出: Vehicle is starting
myBicycle.RingBell(); // 输出: Ring ring!
}
}

在这个例子中,Bicycle 类继承了 Vehicle 类的 Start 方法,因此可以直接调用这个方法。

多态

多态是指同一个方法在不同对象上的不同表现。它通常与方法重写和接口一起使用。C# 中的多态是通过虚方法和重写实现的。

示例

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 class Animal
{
public virtual void Speak()
{
Console.WriteLine("Animal speaks");
}
}

public class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Woof!");
}
}

public class Cat : Animal
{
public override void Speak()
{
Console.WriteLine("Meow!");
}
}

class Program
{
static void Main(string[] args)
{
Animal myAnimal;

myAnimal = new Dog();
myAnimal.Speak(); // 输出: Woof!

myAnimal = new Cat();
myAnimal.Speak(); // 输出: Meow!
}
}

在这个例子中,Animal 类有一个虚方法 Speak。我们在 DogCat 类中重写了 Speak 方法。当我们使用 Animal 类型的引用来调用 Speak 方法时,根据实际对象的类型,它会调用适当的重写方法。

总结

在这一篇中,我们探讨了 C# 面向对象编程的基础概念,包括 对象封装继承多态。这些概念是理解 C# 编程的重要基础。在下一篇中,我们将进一步研究面向对象编程中的属性与方法,继续深入这一主题。希望你在学习这段旅程中获得乐趣,并且能够将这些知识应用于实际编程中!

分享转发

20 属性与方法

在前一篇中,我们讲解了如何定义类以及类的基本结构。接下来,我们将深入探讨面向对象编程中的两个重要概念:属性方法。这些概念构成了我们在使用类时的基本信息和行为。

属性

在C#中,属性是用来封装类中的数据或状态的。它们提供了一种安全的方式来访问和更新类的字段。通过使用属性,我们可以控制字段的访问权限,从而保证数据的安全性。

属性的定义

我们可以通过 C# 关键字 getset 来定义属性。例如,假设我们有一个Person类,它包含NameAge两个属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Person
{
private string name;
private int age;

public string Name
{
get { return name; }
set { name = value; }
}

public int Age
{
get { return age; }
set
{
if (value >= 0) // Age不能为负值
age = value;
else
throw new ArgumentException("Age cannot be negative");
}
}
}

在此案例中,Name属性有一个简单的getset访问器,而Age属性的set访问器中加入了一个条件,以确保年龄不能为负值。

使用属性

使用属性非常简单。可以通过类的实例直接进行读取或赋值操作:

1
2
3
4
5
var person = new Person();
person.Name = "Alice"; // 设置属性
person.Age = 25; // 设置属性

Console.WriteLine($"Name: {person.Name}, Age: {person.Age}"); // 输出属性值

方法

方法是类中定义的行为,定义了在类的实例上可以执行的操作。通过方法,我们可以实现更复杂的逻辑。

方法的定义

下面我们在Person类中添加一个方法Introduce,来让Person实例自我介绍:

1
2
3
4
5
6
7
8
9
public class Person
{
// ...(省略属性定义)

public void Introduce()
{
Console.WriteLine($"Hello, my name is {Name} and I am {Age} years old.");
}
}

在这个例子中,Introduce方法使用了该实例的NameAge属性来输出信息。

调用方法

调用方法与访问属性类似。你只需要使用类的实例来调用方法:

1
2
3
4
5
var person = new Person();
person.Name = "Alice";
person.Age = 25;

person.Introduce(); // 输出:Hello, my name is Alice and I am 25 years old.

总结

在这一篇中,我们探讨了属性和方法的基础知识:

  • 属性用于封装数据,提供安全的访问。
  • 方法定义类的行为,可以执行具体的逻辑。

掌握这些基础后,你将能够构造更复杂的类结构,并为实例定义多种行为。下一篇文章中,我们将继续研究构造函数与析构函数的概念,这对于对象的创建与销毁有着重要作用。

希望本文能帮助你更好地理解 C# 的面向对象编程基础。继续加油!

分享转发

21 面向对象编程基础之构造函数与析构函数

在上一篇中,我们讨论了面向对象编程中的属性与方法。接下来,我们将深入了解构造函数和析构函数,这两个概念在C#中扮演着重要的角色。它们是对象生命周期中至关重要的部分,了解它们能帮助你更好地管理对象的创建与销毁。

什么是构造函数

构造函数是一个特殊的方法,用于初始化类的新实例。构造函数的名称与类名相同,并且没有返回类型。它通常用于设置初始值。

构造函数的基本语法

1
2
3
4
5
6
7
8
9
10
11
12
public class Person
{
public string Name;
public int Age;

// 构造函数
public Person(string name, int age)
{
Name = name;
Age = age;
}
}

构造函数示例

下面是一个具体的示例,展示了如何使用构造函数初始化一个 Person 类的对象。

1
2
3
4
5
6
7
8
9
10
class Program
{
static void Main(string[] args)
{
// 使用构造函数创建一个Person对象
Person person1 = new Person("Alice", 30);

Console.WriteLine($"Name: {person1.Name}, Age: {person1.Age}");
}
}

在这个例子中,我们创建了一个 Person 对象 person1,并通过构造函数将名字和年龄初始化为 “Alice” 和 30。

什么是析构函数

析构函数是一个特殊的方法,用于在对象被垃圾回收前清理资源。析构函数的名称与类名相同,但在前面带有一个波浪号 ~。在C#中,析构函数不需要手动调用,.NET运行时会自动调用它。

析构函数的基本语法

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

// 构造函数
public Person(string name, int age)
{
Name = name;
Age = age;
}

// 析构函数
~Person()
{
// 进行必要的清理工作
Console.WriteLine($"{Name} 的析构函数被调用。");
}
}

析构函数示例

以下是一个完整的示例,演示了当对象被销毁时析构函数的调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Program
{
static void Main(string[] args)
{
{
Person person2 = new Person("Bob", 25);
Console.WriteLine($"Name: {person2.Name}, Age: {person2.Age}");
}

// 这里person2超出范围,程序结束时会调用析构函数
Console.WriteLine("程序结束,开始回收资源。");
}
}

在这个例子中,当 person2 超出作用域时,C# 的垃圾回收机制将会调用 ~Person 的析构函数,输出 "Bob 的析构函数被调用。"

小结

构造函数和析构函数是C#面向对象编程中非常重要的概念。构造函数用于对象的初始化,而析构函数用于对象的清理。在实际开发中,合理地使用构造函数和析构函数能提高代码的可读性和可维护性。

在上一篇中,我们讨论了类中的属性与方法,这些属性与方法通常会在构造函数中进行初始化。同时,在对象不再使用时,析构函数则负责释放资源,确保不会造成内存泄漏。

下一篇中,我们将探讨异常处理,具体内容包括异常的种类及其处理方式。了解异常处理将为你构建更健壮的应用程序打下基础。

分享转发

22 异常处理之异常的种类

在上一篇文章中,我们讨论了面向对象编程中的构造函数与析构函数。今天我们将深入了解异常处理的基础知识,特别是各种异常的种类。异常处理是开发中不可或缺的重要组成部分,它使我们能够在错误发生时优雅地处理程序的行为,而不至于让整个程序崩溃。

什么是异常?

在 C# 中,异常 是一种发生在程序执行过程中的错误情况。它可以是因为代码中的逻辑错误、资源不可用,或者外部系统(如网络、文件等)的故障等引起的。当异常发生时,程序的控制流将暂停,转而使用异常处理机制来处理这个错误。

异常的种类

1. 系统异常(System.Exception)

在 C# 中,所有异常的基类是 System.Exception。自定义的异常通常会继承自这个类。它提供了处理异常的一般结构。

2. 常见的系统异常

C# 提供了多个常见异常类,常见的包括:

  • ArgumentNullException:当一个方法接收到 null 参数,但不允许使用 null

    1
    2
    3
    4
    5
    6
    7
    8
    public void ProcessData(string data)
    {
    if (data == null)
    {
    throw new ArgumentNullException(nameof(data), "数据不能为空");
    }
    // 处理数据...
    }
  • ArgumentOutOfRangeException:表示索引超出了范围。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public void AccessArrayElement(int[] array, int index)
    {
    if (index < 0 || index >= array.Length)
    {
    throw new ArgumentOutOfRangeException(nameof(index), "索引超出范围");
    }

    int value = array[index];
    // 处理值...
    }
  • InvalidOperationException:当方法调用的对象状态不正确时抛出这种异常。

    1
    2
    3
    4
    5
    6
    7
    8
    public void StartProcess(bool isRunning)
    {
    if (isRunning)
    {
    throw new InvalidOperationException("进程已经在运行中");
    }
    // 启动过程...
    }
  • DivideByZeroException:当尝试将一个数除以零时抛出。

    1
    2
    3
    4
    5
    6
    7
    8
    public double Divide(int a, int b)
    {
    if (b == 0)
    {
    throw new DivideByZeroException("不能除以零");
    }
    return a / b;
    }

3. 自定义异常

除了使用系统提供的异常类外,我们可以根据需求自定义异常。自定义异常通常用于表示特定的业务逻辑错误。

1
2
3
4
5
6
public class CustomException : Exception
{
public CustomException(string message) : base(message)
{
}
}

在实际使用中,可以这样抛出自定义异常:

1
2
3
4
5
6
7
8
public void ValidateUser(string username)
{
if (string.IsNullOrEmpty(username))
{
throw new CustomException("用户名不能为空");
}
// 处理用户...
}

4. 捕获异常示例

在了解了异常的种类后,我们将讨论如何捕获和处理这些异常。在下一篇文章中,我们将详细介绍如何使用 try-catch 语句来捕获并处理这些异常。

结语

异常处理是确保 C# 程序健壮性和可靠性的重要手段。理解不同种类的异常以及在什么时候可能会抛出这些异常,可以帮助我们在编写代码时避免潜在的错误。在下一篇文章中,我们将深入探讨异常的捕获和处理机制,帮助你更好地管理程序异常。

分享转发

23 捕获和处理异常

在C#编程中,异常是一种不可预见的错误,可能会导致程序崩溃或不可预期的行为。理解如何有效地捕获和处理这些异常,是编写健壮和可靠程序的关键部分。在本篇中,我们将深入探讨异常的捕获与处理机制,并用具体的案例来说明这些概念。

异常捕获

在C#中,捕获异常通常使用try-catch语句。基本的结构如下所示:

1
2
3
4
5
6
7
8
try
{
// 可能引发异常的代码
}
catch (异常类型 异常对象)
{
// 捕获异常后的处理逻辑
}

示例:捕获整数解析异常

我们来看看一个简单的例子,说明如何捕获并处理解析字符串为整数时可能发生的异常。

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
using System;

class Program
{
static void Main()
{
string input = "abc"; // 非法输入
int number;

try
{
number = int.Parse(input);
Console.WriteLine($"解析结果: {number}");
}
catch (FormatException ex)
{
Console.WriteLine($"解析失败: {ex.Message}");
}
catch (OverflowException ex)
{
Console.WriteLine($"数字溢出: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"其他异常: {ex.Message}");
}

Console.WriteLine("程序继续执行...");
}
}

代码解析

在这个示例中,我们的目标是将字符串input解析为整数。然而,input的值是"abc",这会导致FormatException异常。我们在try块中把可能引发异常的代码放置在一起,并在catch块中处理不同类型的异常。通过这种方式,程序不会崩溃,而是能优雅地处理错误情况。

多个捕获块

在处理异常时,可以有多个catch块来处理不同种类的异常。C#会从上到下检查每个catch块,找到合适的处理程序。一旦找到合适的catch,后续的catch块将被忽略。

示例:多种异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
try
{
// 可能的多种异常代码
string[] array = new string[2];
array[5] = "Hello"; // 引发 IndexOutOfRangeException
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine($"数组索引超出范围: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"捕获到的其他异常: {ex.Message}");
}

在上面的代码中,我们尝试访问数组的非法索引,导致IndexOutOfRangeException。我们不仅处理特定的异常,还提供了一个通用的异常处理机制,以捕捉其他可能的异常。

捕获所有类型的异常

如果你不确定异常的种类,可以使用通用catch,它捕获所有异常。最好将它放在所有具体catch块的最后。

1
2
3
4
5
6
7
8
try
{
// 可能抛出任何异常的代码
}
catch (Exception ex) // 捕获所有类型的异常
{
Console.WriteLine($"发生异常: {ex.Message}");
}

注意事项

尽管能够捕获所有异常很方便,但这也有其缺点。使用通用catch可能会隐藏错误的具体信息。因此,在处理异常时应尽量具体化,优先考虑特定异常的捕获与处理。

清理资源

在某些情况下,即使发生了异常,也需要释放资源。这时,可以使用finally块来确保无论发生了什么,特定的代码总会被执行。

示例:使用finally

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
FileStream fs = null;

try
{
fs = new FileStream("example.txt", FileMode.Open);
// 文件操作代码
}
catch (IOException ex)
{
Console.WriteLine($"文件操作失败: {ex.Message}");
}
finally
{
if (fs != null)
{
fs.Close(); // 确保文件流被关闭
Console.WriteLine("文件流已关闭。");
}
}

在这个例子中,finally块确保无论是否发生异常,文件流fs都能得到正确地关闭,从而避免资源泄漏。

结束语

在本篇中,我们详细探讨了C#中的异常捕获和处理机制,学习了如何使用try-catch语句处理不同的异常类型,以及finally块在资源清理方面的作用。在下一篇中,我们将进一步了解自定义异常,以便灵活地处理特定的错误情境。希望你能通过实践和不断的编码,掌握异常处理的精髓!

分享转发

24 异常处理之自定义异常

在上篇中,我们学习了如何捕获和处理异常,这是异常处理的基础。在这篇文章中,我们将深入探讨如何创建和使用自定义异常。这将在我们的异常处理能力中起到重要的作用,尤其是在需要特定错误信息的情况下。

什么是自定义异常?

自定义异常是我们可以根据需要自己定义的异常类型,这使得我们在处理特定错误时能够更加清晰和易于维护。通常情况下,当我们在应用程序中遇到特定的错误条件时,使用自定义异常可以提供更有意义的错误信息。

如何创建自定义异常

在 C# 中,创建自定义异常只需创建一个继承自 System.Exception 类的新类。下面是一个简单的自定义异常类的例子:

1
2
3
4
5
6
7
8
9
10
using System;

public class MyCustomException : Exception
{
public MyCustomException() { }

public MyCustomException(string message) : base(message) { }

public MyCustomException(string message, Exception inner) : base(message, inner) { }
}

在这个例子中,MyCustomException 继承自 Exception 类,并提供了三个构造函数,以支持不同的初始化方式。

使用自定义异常

抛出自定义异常

在合适的地方抛出自定义异常,可以使你的代码更加清晰。下面是一个简单的使用自定义异常的例子:

1
2
3
4
5
6
7
8
9
10
11
public class Calculator
{
public int Divide(int numerator, int denominator)
{
if (denominator == 0)
{
throw new MyCustomException("分母不能为零。");
}
return numerator / denominator;
}
}

在这个例子中,Divide 方法在检测到分母为零时,抛出了 MyCustomException

捕获自定义异常

捕获自定义异常与捕获标准异常没有区别。以下是如何捕获自定义异常的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Program
{
public static void Main()
{
Calculator calculator = new Calculator();

try
{
int result = calculator.Divide(10, 0);
Console.WriteLine($"结果是: {result}");
}
catch (MyCustomException ex)
{
Console.WriteLine($"发生了自定义异常: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"发生了其他异常: {ex.Message}");
}
}
}

在这个示例中,我们尝试将 10 除以 0,这将触发 MyCustomException。在 catch 块中捕获到此自定义异常后,我们打印相应的错误消息。

自定义异常的优势

  1. 清晰的错误信息:通过自定义异常,可以提供更明确的错误消息,有助于调试。
  2. 特定的错误类型:自定义异常可以帮助我们对不同类型的错误进行分类处理。
  3. 易维护性:当自定义错误类型变得复杂时,可以集中处理特定的异常,提高代码的可维护性。

小结

自定义异常是 C# 异常处理机制中的一个重要部分,它使得我们能够在应用程序中更有效地处理特定类型的错误。在本篇中,我们学习了如何创建自定义异常类、抛出自定义异常,以及如何捕获自定义异常。

接下来,我们将在下篇中探讨集合和数组的使用,开始我们的数据结构学习之旅。希望你已经对异常处理有了更深刻的理解,并能在实际编程中灵活应用自定义异常。

分享转发