语句
C# 作为一种现代化的面向对象编程语言,支持多种类型的语句(Statements),这些语句用于构建程序的逻辑控制结构、数据操作和流程控制。
类别 | C# 关键字 | 说明 |
---|---|---|
声明语句 | - | 声明语句引入新的变量或常量。 变量声明可以选择为变量赋值。 在常量声明中必须赋值。 |
表达式语句 | - | 用于计算值的表达式语句必须在变量中存储该值。 |
选择语句 | if,switch | 选择语句用于根据一个或多个指定条件分支到不同的代码段。 |
迭代语句 | for,foreach,while,do-while | 迭代语句用于遍历集合(如数组),或重复执行同一组语句直到满足指定的条件 |
跳转语句 | break,continue,goto,return,yield | 跳转语句将控制转移给另一代码段。 |
异常处理语句 | throw,try-catch,try-finally,try-catch-finally | 异常处理语句用于从运行时发生的异常情况正常恢复。 |
checked,unchecked语句 | - | checked 和 unchecked 语句用于指定将结果存储在变量中、但该变量过小而不能容纳结果值时,是否允许整型数值运算导致溢出。 |
await 语句 | - | 如果用 async 修饰符标记方法,则可以使用该方法中的 await 运算符。 在控制到达异步方法的 await 表达式时,控制将返回到调用方,该方法中的进程将挂起,直到等待的任务完成为止。 任务完成后,可以在方法中恢复执行。 |
fixed 语句 | - | fixed 语句禁止垃圾回收器重定位可移动的变量。 有关详细信息,请参阅 fixed。 |
lock 语句 | - | lock 语句用于限制一次仅允许一个线程访问代码块。 有关详细信息,请参阅 lock。 |
空语句 | - | 空语句只含一个分号。 不执行任何操作,可以在需要语句但不需要执行任何操作的地方使用。 |
声明语句
声明语句用于定义变量、常量、方法、类、结构等
// 变量声明
double area;
double radius = 2;
// 一个语句中声明多个相同类型的变量
int a, b, c;
// 常量声明
const double pi = 3.14159;
隐式类型声明
- 使用 var 关键字
var greeting = "Hello";
var a = 32;
var xs = new List<double>();
- 目标类型化 new
List<int> xs = new();
List<int>? ys = new();
- 使用匿名类型
var fromPhoenix = from cust in customers
where cust.City == "Phoenix"
select new { cust.Name, cust.Phone };
foreach (var customer in fromPhoenix)
{
Console.WriteLine($"Name={customer.Name}, Phone={customer.Phone}");
}
表达式语句
表达式语句是执行表达式并计算的一种语句。 参考表达式
选择语句
选择语句(也称为条件语句)用于根据表达式的结果或变量的值来控制程序的执行流程。
if 语句
if 语句用于在条件为 true 时执行某段代码。
int number = 5;
if (number > 10)
{
Console.WriteLine("Number is greater than 10.");
}
else if (number == 5)
{
Console.WriteLine("Number is exactly 5.");
}
else
{
Console.WriteLine("Number is less than 10 but not 5.");
}
switch 语句
switch 语句用于根据一个变量的值来选择执行不同的代码块。它通常用于替代多个 else if 语句。
int day = 3;
switch (day)
{
case 1:
Console.WriteLine("Monday");
break;
case 2:
Console.WriteLine("Tuesday");
break;
case 3:
Console.WriteLine("Wednesday");
break;
case 4:
Console.WriteLine("Thursday");
break;
case 5:
Console.WriteLine("Friday");
break;
default:
Console.WriteLine("Weekend");
break;
}
//简洁写法(C# 8.0 引入)
int day = 3;
string dayName = day switch
{
1 => "Monday",
2 => "Tuesday",
3 => "Wednesday",
4 => "Thursday",
5 => "Friday",
_ => "Weekend",
};
更多switch 语句支持的模式的信息,参考模式匹配
迭代语句
迭代语句用于反复执行某段代码,直到满足某个条件。C# 提供了几种常见的迭代语句,包括 for、foreach、while 和 do-while。
for 语句
在指定的布尔表达式的计算结果为 true 时,for 语句会执行一条语句或一个语句块。
for (int i = 0; i < 3; i++)
{
Console.Write(i);
}
// Output:
// 012
示例展示了 for 语句的元素
- 初始化表达式: int i = 0;
- 条件: i < 3;
- 迭代器: i++;
- 循环体:
{ Console.Write(i); }
迭代器
迭代器”部分可包含用逗号分隔的零个或多个以下语句表达式:
- 为 increment 表达式添加前缀或后缀,如 ++i 或 i++
- 为 decrement 表达式添加前缀或后缀,如 --i 或 i--
- 赋值,如 i = k
- 方法的调用,如 i = add(a, b)
- await表达式, 如 await client.GetByteArrayAsync(url);
- 通过使用 new 运算符来创建对象
for 语句的所有部分都是可选的。 例如,以下代码定义无限 for 循环:
for ( ; ; )
{
//...
}
foreach 语句
foreach 语句为类型实例中实现 System.Collections.IEnumerable 或 System.Collections.Generic.IEnumerable<T> 接口的每个元素执行语句或语句块
List<int> fibNumbers = new() { 0, 1, 1, 2, 3, 5, 8, 13 };
foreach (int element in fibNumbers)
{
Console.Write($"{element} ");
}
// Output:
// 0 1 1 2 3 5 8 13
// ref 或 ref readonly 修饰符声明迭代变量
Span<int> storage = stackalloc int[10];
int num = 0;
foreach (ref int item in storage)
{
item = num++;
}
foreach (ref readonly var item in storage)
{
Console.Write($"{item} ");
}
// Output:
// 0 1 2 3 4 5 6 7 8 9
//await foreach
await foreach (var item in GenerateSequenceAsync())
{
Console.WriteLine(item);
}
foreach 语句并不限于这些类型
可以将其与满足以下条件的任何类型的实例一起使用:
- 类型具有公共无参数 GetEnumerator 方法。 GetEnumerator 方法可以是类型的扩展方法。
- GetEnumerator 方法的返回类型具有公共 Current 属性和公共无参数 MoveNext 方法(其返回类型为 bool)。
while 语句
在指定的布尔表达式的计算结果为 true 时,while 语句会执行一条语句或一个语句块。
在每次执行循环之前都会计算此表达式,while 循环会执行零次或多次。
while 循环不同于 do 循环(该循环执行 1 次或多次)。
int n = 0;
while (n < 5)
{
Console.Write(n);
n++;
}
// Output:
// 01234
do 语句
在指定的布尔表达式的计算结果为 true 时,do 语句会执行一条语句或一个语句块。
由于在每次执行循环之后都会计算此表达式,所以 do 循环会执行一次或多次。
do 循环不同于 while 循环(该循环执行零次或多次)。
int n = 0;
do
{
Console.Write(n);
n++;
} while (n < 5);
// Output:
// 01234
跳转语句
跳转语句用于控制程序的执行流,通常用来提前终止循环、方法或直接跳转到特定的代码部分
break 语句
break 语句用于立即终止最内层的循环或 switch 语句,并跳出该结构。
break 语句通常用在 for、while、do-while 循环和 switch 语句中。
for (int i = 0; i < 10; i++)
{
if (i == 5)
break; // 退出循环,当 i == 5 时
Console.WriteLine(i);
}
continue 语句
continue 语句跳过当前循环的剩余代码,直接开始下一次迭代。
它在循环结构中常用,用于在满足某些条件时跳过循环中的部分代码。
for (int i = 0; i < 10; i++)
{
if (i % 2 == 0)
continue; // 跳过偶数,继续下一次循环
Console.WriteLine(i);
}
return 语句
return 语句用于终止当前方法的执行,并返回到调用该方法的位置。
如果方法有返回值,return 语句可以指定要返回的值。
int Add(int a, int b)
{
return a + b; // 返回两个数的和
}
void PrintEvenNumbers(int max)
{
for (int i = 0; i < max; i++)
{
if (i % 2 != 0)
continue; // 只打印偶数
Console.WriteLine(i);
}
}
int sum = Add(3, 4); // sum = 7
Console.WriteLine($"Sum: {sum}");
PrintEvenNumbers(10); // 打印 0, 2, 4, 6, 8
goto 语句
goto 语句无条件地跳转到代码中的指定标签位置。尽管 goto 语句可以在某些情况下提供灵活性,但它通常会导致代码难以理解和维护,因此应谨慎使用
int x = 10;
if (x > 5)
{
goto Label1; // 跳转到 Label1 位置
}
Console.WriteLine("This line will be skipped.");
Label1:
Console.WriteLine("Jumped to Label1");
yield break 语句
yield break 语句用于立即终止迭代器方法的迭代。
与 return 不同,yield break 仅用于迭代器方法中,用来提前结束生成序列。
IEnumerable<int> GetNumbers()
{
yield return 1;
yield return 2;
yield break; // 提前终止迭代
yield return 3; // 这行代码不会被执行
}
foreach (int number in GetNumbers())
{
Console.WriteLine(number); // 输出 1, 2
}
yield return 语句
yield return 语句用于迭代器方法中,按顺序返回每个元素。
与 return 语句不同,yield return 允许方法返回一个集合中的多个值,通常用于实现自定义迭代器。
IEnumerable<int> GetEvenNumbers(int max)
{
for (int i = 0; i <= max; i++)
{
if (i % 2 == 0)
yield return i; // 返回当前偶数
}
}
foreach (int number in GetEvenNumbers(10))
{
Console.WriteLine(number); // 输出 0, 2, 4, 6, 8, 10
}
异常处理语句
异常处理语句用于从运行时发生的异常情况正常恢复。 参考异常
checked,unchecked语句
checked 和 unchecked 语句指定整型类型算术运算和转换的溢出检查上下文。
当发生整数算术溢出时,溢出检查上下文将定义发生的情况。
在已检查的上下文中,引发 System.OverflowException;如果在常数表达式中发生溢出,则会发生编译时错误。
在未检查的上下文中,会通过丢弃任何不适应目标类型的高序位来将操作结果截断。
uint a = uint.MaxValue;
unchecked
{
Console.WriteLine(a + 3); // output: 2
}
try
{
checked
{
Console.WriteLine(a + 3);
}
}
catch (OverflowException e)
{
Console.WriteLine(e.Message); // output: Arithmetic operation resulted in an overflow.
}
await 语句
如果用 async 修饰符标记方法,则可以使用该方法中的 await 运算符。 在控制到达异步方法的 await 表达式时,控制将返回到调用方,该方法中的进程将挂起,直到等待的任务完成为止。 任务完成后,可以在方法中恢复执行。
参考异步编程
fixed 语句
fixed 语句用于将对象的内存地址固定住,使得垃圾回收器(GC)在 fixed 语句块中不会移动该对象。它通常用于处理指针和非托管代码,确保对象在操作期间保持在内存中的固定位置。
固定数组的内存地址
fixed 语句确保数组 numbers 在循环体中不会被垃圾回收器移动,使得指针 ptr 可以安全地操作该数组。
unsafe // 必须使用 unsafe 关键字,因为指针操作是不安全的
{
int[] numbers = { 1, 2, 3, 4, 5 };
fixed (int* ptr = numbers)
{
for (int i = 0; i < numbers.Length; i++)
{
*(ptr + i) = *(ptr + i) * 2; // 通过指针修改数组元素
}
}
foreach (int num in numbers)
{
Console.WriteLine(num); // 输出:2, 4, 6, 8, 10
}
}
固定字符串的内存地址
字符串在 C# 中是不可变的(immutable),但是在使用非托管代码时,可能需要访问字符串的底层字符数组。
unsafe
{
string text = "Hello, World!";
fixed (char* ptr = text)
{
for (int i = 0; i < text.Length; i++)
{
Console.WriteLine(*(ptr + i)); // 输出字符串的每个字符
}
}
}
注意事项
- unsafe 关键字:使用 fixed 语句的代码块必须标记为 unsafe,因为指针操作被认为是不安全的。
- 性能:fixed 语句会降低垃圾回收器的效率,因为它会限制对象的移动,因此应仅在必要时使用。
- 指针指向的对象类型:fixed 语句只适用于托管数组、字符串以及在托管堆上分配的引用类型的字段。
lock 语句
lock 语句用于限制一次仅允许一个线程访问代码块。
参考异步编程
空语句
空语句只含一个分号。 不执行任何操作,可以在需要语句但不需要执行任何操作的地方使用。