跳转语句(C# 参考)
以下语句无条件转移控制:
break
语句:将终止最接近的封闭迭代语句或switch
语句。continue
语句:启动最接近的封闭迭代语句的新迭代。return
语句:可终止它所在的函数的执行,并将控制权返回给调用方。return
语句上的ref
修饰符表示返回的表达式是按引用返回的,而不是按值返回的。goto
语句:将控制权转交给带有标签的语句。
有关引发异常并无条件转移控制权的 throw
语句的信息,请参阅 throw。
break
语句
break
语句:将终止最接近的封闭迭代语句(即 for
、foreach
、while
或 do
循环)或 switch
语句。 break
语句将控制权转交给已终止语句后面的语句(若有)。
int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
foreach (int number in numbers)
{
if (number == 3)
{
break;
}
Console.Write($"{number} ");
}
Console.WriteLine();
Console.WriteLine("End of the example.");
// Output:
// 0 1 2
// End of the example.
在嵌套循环中,break
语句仅终止包含它的最内部循环,如以下示例所示:
for (int outer = 0; outer < 5; outer++)
{
for (int inner = 0; inner < 5; inner++)
{
if (inner > outer)
{
break;
}
Console.Write($"{inner} ");
}
Console.WriteLine();
}
// Output:
// 0
// 0 1
// 0 1 2
// 0 1 2 3
// 0 1 2 3 4
在循环内使用 switch
语句时,switch 节末尾的 break
语句仅从 switch
语句中转移控制权。 包含 switch
语句的循环不受影响,如以下示例所示:
double[] measurements = { -4, 5, 30, double.NaN };
foreach (double measurement in measurements)
{
switch (measurement)
{
case < 0.0:
Console.WriteLine($"Measured value is {measurement}; too low.");
break;
case > 15.0:
Console.WriteLine($"Measured value is {measurement}; too high.");
break;
case double.NaN:
Console.WriteLine("Failed measurement.");
break;
default:
Console.WriteLine($"Measured value is {measurement}.");
break;
}
}
// Output:
// Measured value is -4; too low.
// Measured value is 5.
// Measured value is 30; too high.
// Failed measurement.
continue
语句
continue
语句启动最接近的封闭迭代语句(即 for
、foreach
、while
或 do
循环)的新迭代,如以下示例所示:
for (int i = 0; i < 5; i++)
{
Console.Write($"Iteration {i}: ");
if (i < 3)
{
Console.WriteLine("skip");
continue;
}
Console.WriteLine("done");
}
// Output:
// Iteration 0: skip
// Iteration 1: skip
// Iteration 2: skip
// Iteration 3: done
// Iteration 4: done
return
语句
return
语句终止它所在的函数的执行,并将控制权和函数结果(若有)返回给调用方。
如果函数成员不计算值,则使用不带表达式的 return
语句,如以下示例所示:
Console.WriteLine("First call:");
DisplayIfNecessary(6);
Console.WriteLine("Second call:");
DisplayIfNecessary(5);
void DisplayIfNecessary(int number)
{
if (number % 2 == 0)
{
return;
}
Console.WriteLine(number);
}
// Output:
// First call:
// Second call:
// 5
如前面的示例所示,通常使用不带表达式的 return
语句提前终止函数成员。 如果函数成员不包含 return
语句,则在执行其最后一个语句后终止。
如果函数成员不计算值,则使用带表达式的 return
语句,如以下示例所示:
double surfaceArea = CalculateCylinderSurfaceArea(1, 1);
Console.WriteLine($"{surfaceArea:F2}"); // output: 12.57
double CalculateCylinderSurfaceArea(double baseRadius, double height)
{
double baseArea = Math.PI * baseRadius * baseRadius;
double sideArea = 2 * Math.PI * baseRadius * height;
return 2 * baseArea + sideArea;
}
如果 return
语句具有表达式,该表达式必须可隐式转换为函数成员的返回类型,除非它是异步的。 从 async
函数返回的表达式必须隐式转换为 Task<TResult> 或 ValueTask<TResult> 类型参数,以函数的返回类型为准。 如果 async
函数的返回类型为 Task 或 ValueTask,则使用不带表达式的 return
语句。
默认情况下,return
语句返回表达式的值。 可以返回对变量的引用。 为此,请使用带 ref
关键字的 return
语句,如以下示例所示:
var xs = new int[] { 10, 20, 30, 40 };
ref int found = ref FindFirst(xs, s => s == 30);
found = 0;
Console.WriteLine(string.Join(" ", xs)); // output: 10 20 0 40
ref int FindFirst(int[] numbers, Func<int, bool> predicate)
{
for (int i = 0; i < numbers.Length; i++)
{
if (predicate(numbers[i]))
{
return ref numbers[i];
}
}
throw new InvalidOperationException("No element satisfies the given condition.");
}
引用返回
返回值可以通过引用返回(ref
返回)。 借助引用返回值,方法可以将对变量的引用(而不是值)返回给调用方。 然后,调用方可以选择将返回的变量视为按值返回或按引用返回。 调用方可以新建称为引用本地的变量,其本身就是对返回值的引用。 引用返回值是指,方法返回对某变量的引用(或别名)。 相应变量的作用域必须包括方法。 相应变量的生存期必须超过方法的返回值。 调用方对方法的返回值进行的修改应用于方法返回的变量。
如果声明方法返回引用返回值,表明方法返回变量别名。 设计意图通常是让调用代码通过别名访问此变量(包括修改它)。 方法的引用返回值不得包含返回类型 void
。
ref
返回值是被调用方法范围中另一个变量的别名。 可以将引用返回值的所有使用都解释为,使用它取别名的变量:
- 分配值时,就是将值分配到它取别名的变量。
- 读取值时,就是读取它取别名的变量的值。
- 如果以引用方式返回它,就是返回对相同变量所取的别名。
- 如果以引用方式将它传递到另一个方法,就是传递对它取别名的变量的引用。
- 如果返回引用本地别名,就是返回相同变量的新别名。
引用返回必须是 ref_safe_to_escape 到调用方法。 也就是说:
- 返回值的生存期必须长于方法执行时间。 换言之,它不能是返回自身的方法中的本地变量。 它可以是实例或类的静态字段,也可是传递给方法的参数。 尝试返回局部变量将生成编译器错误 CS8168:“无法按引用返回局部 "obj",因为它不是 ref 局部变量”。
- 返回值不得为文本
null
。 使用引用返回值的方法可以返回值当前为null
(未实例化)或可为空的值类型的变量别名。 - 返回值不得为常量、枚举成员、通过属性的按值返回值或
class
/struct
方法。
此外,禁止对异步方法使用引用返回值。 异步方法可能会在执行尚未完成时就返回值,尽管返回值仍未知。
返回引用返回值的方法必须:
下面的示例方法满足这些条件,且返回对名为 p
的 Person
对象的引用:
public ref Person GetContactInformation(string fname, string lname)
{
// ...method implementation...
return ref p;
}
goto
语句
goto
语句将控制权转交给带有标签的语句,如以下示例所示:
var matrices = new Dictionary<string, int[][]>
{
["A"] = new[]
{
new[] { 1, 2, 3, 4 },
new[] { 4, 3, 2, 1 }
},
["B"] = new[]
{
new[] { 5, 6, 7, 8 },
new[] { 8, 7, 6, 5 }
},
};
CheckMatrices(matrices, 4);
void CheckMatrices(Dictionary<string, int[][]> matrixLookup, int target)
{
foreach (var (key, matrix) in matrixLookup)
{
for (int row = 0; row < matrix.Length; row++)
{
for (int col = 0; col < matrix[row].Length; col++)
{
if (matrix[row][col] == target)
{
goto Found;
}
}
}
Console.WriteLine($"Not found {target} in matrix {key}.");
continue;
Found:
Console.WriteLine($"Found {target} in matrix {key}.");
}
}
// Output:
// Found 4 in matrix A.
// Not found 4 in matrix B.
如前面的示例所示,可以使用 goto
语句退出嵌套循环。
提示
使用嵌套循环时,请考虑将单独的循环重构为单独的方法。 这可能会导致没有 goto
语句的更简单、更具可读性的代码。
还可使用 switch
语句中的 goto
语句将控制权移交到具有常量大小写标签的 switch 节,如以下示例所示:
using System;
public enum CoffeChoice
{
Plain,
WithMilk,
WithIceCream,
}
public class GotoInSwitchExample
{
public static void Main()
{
Console.WriteLine(CalculatePrice(CoffeChoice.Plain)); // output: 10.0
Console.WriteLine(CalculatePrice(CoffeChoice.WithMilk)); // output: 15.0
Console.WriteLine(CalculatePrice(CoffeChoice.WithIceCream)); // output: 17.0
}
private static decimal CalculatePrice(CoffeChoice choice)
{
decimal price = 0;
switch (choice)
{
case CoffeChoice.Plain:
price += 10.0m;
break;
case CoffeChoice.WithMilk:
price += 5.0m;
goto case CoffeChoice.Plain;
case CoffeChoice.WithIceCream:
price += 7.0m;
goto case CoffeChoice.Plain;
}
return price;
}
}
在 switch
语句中,还可使用语句 goto default;
将控制权转交给带 default
标签的 switch 节。
如果当前函数成员中不存在具有给定名称的标签,或者 goto
语句不在标签范围内,则会出现编译时错误。 也就是说,你不能使用 goto
语句将控制权从当前函数成员转移到任何嵌套范围(例如 try
块)。
C# 语言规范
有关更多信息,请参阅 C# 语言规范的以下部分: