Поделиться через


Операторы перехода — break, continue, returnи goto

Операторы перехода безоговорочно передают управление. Оператор break завершает ближайшее заключающее оператор итерации или switch инструкцию. Оператор continue запускает новую итерацию ближайшего заключенного оператора итерации. Операторreturn завершает выполнение функции, в которой она отображается, и возвращает элемент управления вызывающему объекту. Оператор goto передает элемент управления инструкции, помеченной меткой.

Сведения об throw инструкции, которая вызывает исключение и безусловно передает управление, смthrow. в разделе инструкций обработки исключений статьи.

Инструкция break

Оператор break завершает ближайшее заключающее оператор итерации (т. е. , for, foreachwhileили цикл) или doswitch оператор. Оператор 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, foreachwhileили 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 функции имеет или ValueTaskиспользуется Taskreturn оператор без выражения.

Возврат ссылок

По умолчанию return инструкция возвращает значение выражения. Вы можете вернуть ссылку на переменную. Ссылочные возвращаемые значения (или возвращаемые значения ссылок) — это значения, возвращаемые методом по ссылке на вызывающий объект. То есть вызывающий объект может изменить значение, возвращаемое методом, и это изменение отражается в состоянии объекта в вызываемом методе. Для этого используйте инструкцию return с ref ключевым словом, как показано в следующем примере:

int[] 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.");
}

Возвращаемое значение ссылки позволяет методу возвращать ссылку на переменную, а не значение обратно вызывающей стороне. Вызывающий объект может затем обработать возвращаемую переменную, как если бы она возвращалась по значению или по ссылке. Вызывающий объект может создать новую переменную, которая является ссылкой на возвращаемое значение, называемое локальным ссылкой. Возвращаемое значение ссылки означает, что метод возвращает ссылку (или псевдоним) в некоторую переменную. Область этой переменной должна включать метод. Время существования этой переменной должно превышать возвращение метода. Изменения возвращаемого значения метода вызывающим методом вносятся в переменную, возвращаемую методом.

Объявление того, что метод возвращает возвращаемое значение ссылки , указывает, что метод возвращает псевдоним переменной. Цель разработки часто заключается в том, что вызов кода обращается к этой переменной с помощью псевдонима, в том числе для изменения. Методы, возвращаемые по ссылке, не могут иметь тип возвращаемого значения void.

Чтобы вызывающий объект изменял состояние объекта, возвращаемое значение ссылки должно храниться в переменной, которая явно определяется как эталонная переменная.

Возвращаемое ref значение — это псевдоним другой переменной в области вызываемого метода. Вы можете интерпретировать любое использование возвращаемого возвращаемого ссылки как с помощью переменной, с помощью ее псевдонима:

  • При назначении его значения вы назначаете значение переменной, которую он псевдонимирует.
  • При чтении его значения вы считываете значение переменной, которую он псевдонимирует.
  • Если вы возвращаете его по ссылке, вы возвращаете псевдоним в ту же переменную.
  • Если передать его другому методу по ссылке, вы передаете ссылку на переменную, которую он псевдонимирует.
  • При создании локального псевдонима ссылки вы создайте новый псевдоним в той же переменной.

Возвращаемое значение ref должно быть ref-safe-context для вызывающего метода. Это означает:

  • Возвращаемое значение должно иметь время существования, которое выходит за рамки выполнения метода. Другими словами, это не может быть локальная переменная в методе, который возвращает его. Это может быть экземпляр или статическое поле класса, или это может быть аргумент, переданный методу. Попытка вернуть локальную переменную создает ошибку компилятора CS8168, "Не удается вернуть локальный obj по ссылке, так как он не является локальным.
  • Возвращаемое значение не может быть литеральным null. Метод с возвратом ссылок может возвращать псевдоним переменной, значение которой в настоящее время null является значением (неинстанизированным) или типом значения NULL для типа значения.
  • Возвращаемое значение не может быть константой, элементом перечисления, возвращаемым значением из свойства или методом или classstruct.

Кроме того, значения возвращаемых ссылок не допускаются для асинхронных методов. Асинхронный метод может вернуться до завершения выполнения, а его возвращаемое значение по-прежнему неизвестно.

Метод, возвращающий возвращаемое значение ссылки , должен:

  • Включите ключевое слово ref перед типом возвращаемого значения.
  • Каждая инструкция return в теле метода включает ключевое слово ref перед именем возвращаемого экземпляра.

В следующем примере показан метод, который удовлетворяет этим условиям и возвращает ссылку на Person объект с именем p:

public ref Person GetContactInformation(string fname, string lname)
{
    // ...method implementation...
    return ref p;
}

Ниже приведен более полный пример возврата ссылок, показывающий как сигнатуру метода, так и текст метода.

public static ref int Find(int[,] matrix, Func<int, bool> predicate)
{
    for (int i = 0; i < matrix.GetLength(0); i++)
        for (int j = 0; j < matrix.GetLength(1); j++)
            if (predicate(matrix[i, j]))
                return ref matrix[i, j];
    throw new InvalidOperationException("Not found");
}

Вызываемый метод также может объявить возвращаемое значение как ref readonly возвращаемое значение по ссылке, и применить, что вызывающий код не может изменить возвращаемое значение. Вызывающий метод может избежать копирования возвращаемого значения, сохраняя значение в локальной ref readonly эталонной переменной.

В следующем примере определяется Book класс с двумя String полями Title и Author. Он также определяет BookCollection класс, включающий частный массив Book объектов. Отдельные объекты книг возвращаются по ссылке путем вызова метода GetBookByTitle .


public class Book
{
    public string Author;
    public string Title;
}

public class BookCollection
{
    private Book[] books = { new Book { Title = "Call of the Wild, The", Author = "Jack London" },
                        new Book { Title = "Tale of Two Cities, A", Author = "Charles Dickens" }
                       };
    private Book nobook = null;

    public ref Book GetBookByTitle(string title)
    {
        for (int ctr = 0; ctr < books.Length; ctr++)
        {
            if (title == books[ctr].Title)
                return ref books[ctr];
        }
        return ref nobook;
    }

    public void ListBooks()
    {
        foreach (var book in books)
        {
            Console.WriteLine($"{book.Title}, by {book.Author}");
        }
        Console.WriteLine();
    }
}

Когда вызывающий объект сохраняет значение, возвращаемое GetBookByTitle методом в виде локального ссылки, изменения, внесенные вызывающим значением в возвращаемое значение, отражаются в объекте, как показано в BookCollection следующем примере.

var bc = new BookCollection();
bc.ListBooks();

ref var book = ref bc.GetBookByTitle("Call of the Wild, The");
if (book != null)
    book = new Book { Title = "Republic, The", Author = "Plato" };
bc.ListBooks();
// The example displays the following output:
//       Call of the Wild, The, by Jack London
//       Tale of Two Cities, A, by Charles Dickens
//
//       Republic, The, by Plato
//       Tale of Two Cities, A, by Charles Dickens

Инструкция goto

Оператор goto передает управление инструкции, помеченной меткой, как показано в следующем примере:

var matrices = new Dictionary<string, int[][]>
{
    ["A"] =
    [
        [1, 2, 3, 4],
        [4, 3, 2, 1]
    ],
    ["B"] =
    [
        [5, 6, 7, 8],
        [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 .

Инструкцию goto в switch инструкции можно также использовать для передачи управления в раздел switch с меткой регистра константы, как показано в следующем примере:

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 для передачи управления из текущего элемента функции или в любую вложенную область.

Спецификация языка C#

Дополнительные сведения см. в следующих разделах статьи Спецификация языка C#:

См. также