Операторы перехода — break
, continue
, return
и goto
Четыре оператора C# безоговорочно передают управление. Оператор break
завершает ближайший оператор или операторитерацииswitch
. Операторcontinue
запускает новую итерацию ближайшего включающего оператора итерации. Операторreturn
: завершает выполнение функции, в которой она отображается, и возвращает управление вызывающему объекту. Модификатор ref
в операторе return
указывает, что возвращаемое выражение возвращается по ссылке, а не по значению. Операторgoto
передает управление инструкции, помеченной меткой.
Сведения об операторе 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
внутри цикла оператор break
в конце раздела switch передает управление только из оператора 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
возвращает значение выражения. Вы можете вернуть ссылку на переменную. Для этого используйте оператор return
с ключевым словом ref
, как показано в следующем примере:
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
return). Возвращаемое ссылочное значение позволяет методу вернуть вызывающей стороне ссылку на переменную, а не фиксированное значение. После этого вызывающий может самостоятельно решить, как обрабатывать полученную переменную: по значению или по ссылке. Вызывающий объект может создать новую переменную, которая сама по себе является ссылкой на возвращаемое значение, называемое ref local. Возвращаемое ссылочное значение означает, что метод возвращает ссылку на некоторую переменную (или ее псевдоним). В область действия переменной должен входить этот метод. Время существования переменной должно продолжаться после того, как метод возвращает управление. Все изменения, которые вызывающий производит с возвращаемым значением метода, применяются к возвращенной переменной.
Если для метода объявлено возвращаемое ссылочное значение, значит он возвращает псевдоним переменной. Цель проектирования часто заключается в том, что вызывающий код обращается к этой переменной через псевдоним, в том числе для ее изменения. Методы, возвращающие по ссылке, не могут иметь тип возвращаемого значения void
.
Возвращаемое ref
значение является псевдонимом другой переменной в области вызываемого метода. Любое применение возвращаемого ссылочного значения можно рассматривать как применение псевдонима соответствующей переменной.
- При назначении его значения вы присваиваете значение переменной, которую он псевдонимирует.
- При чтении его значения вы считываете значение переменной, которую он псевдонимирует.
- Если вы возвращаете его по ссылке, вы возвращаете псевдоним той же переменной.
- Если вы передаете его в другой метод по ссылке, вы передаете ссылку на переменную, которую он псевдонимирует.
- Создавая для псевдонима локальную ссылочную переменную, вы создаете новый псевдоним для той же переменной.
Возвращаемый аргумент ref должен быть ref_safe_to_escape в вызывающий метод. Это означает:
- Время существования возвращаемого значения должно превышать период выполнения метода. Другими словами, она не может быть локальной переменной в методе, который возвращает ее. Это может быть экземпляр статического поля или класса, а также переданный в метод аргумент. Попытка вернуть локальную переменную приводит к ошибке компилятора CS8168 : "Не удается вернуть локальный obj по ссылке, так как он не является ссылочным локальным".
- Возвращаемое значение не может быть литеральным
null
. Метод с возвращаемым значением ref может возвращать псевдоним переменной, значение которой в настоящее время является значениемnull
(uninstantiated) или типом значения, допускающего значение NULL для типа значения. - Возвращаемое значение не может быть константой, элементом перечисления, возвращаемым значением по значению из свойства или методом
class
илиstruct
.
Кроме того, возвращаемые значения ссылок не допускаются в асинхронных методах. Асинхронный метод может вернуть управление до того, как будет завершено его выполнение и станет известно его возвращаемое значение.
Метод, возвращающий возвращаемое значение ссылки , должен:
- Включите ключевое слово ref перед типом возвращаемого значения.
- Каждый оператор return в теле метода включает ключевое слово ref перед именем возвращаемого экземпляра.
В следующем примере показан метод, который удовлетворяет указанным условиям и возвращает ссылку на объект Person
с именем p
:
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 с меткой константы case, как показано в следующем примере:
using System;
public enum CoffeeChoice
{
Plain,
WithMilk,
WithIceCream,
}
public class GotoInSwitchExample
{
public static void Main()
{
Console.WriteLine(CalculatePrice(CoffeeChoice.Plain)); // output: 10.0
Console.WriteLine(CalculatePrice(CoffeeChoice.WithMilk)); // output: 15.0
Console.WriteLine(CalculatePrice(CoffeeChoice.WithIceCream)); // output: 17.0
}
private static decimal CalculatePrice(CoffeeChoice choice)
{
decimal price = 0;
switch (choice)
{
case CoffeeChoice.Plain:
price += 10.0m;
break;
case CoffeeChoice.WithMilk:
price += 5.0m;
goto case CoffeeChoice.Plain;
case CoffeeChoice.WithIceCream:
price += 7.0m;
goto case CoffeeChoice.Plain;
}
return price;
}
}
В операторе switch
можно также использовать оператор goto default;
для передачи управления в раздел switch с меткой default
.
Если метка с заданным именем не существует в текущем элементе функции или инструкция goto
не входит в область действия метки, возникает ошибка во время компиляции. Это значит, что оператор goto
нельзя использовать для передачи управления из текущего члена функции или во вложенную область, например в блок try
.
Спецификация языка C#
Дополнительные сведения см. в следующих разделах статьи Спецификация языка C#: