Compartilhar via


Instruções de salto – break, , returncontinueegoto

As instruções de salto transferem incondicionalmente o controle. A break instrução encerra a instrução ouswitch instrução de iteração de fechamento mais próxima. A continue instrução inicia uma nova iteração da instrução de iteração delimitando mais próxima. A return instrução encerra a execução da função na qual ela aparece e retorna o controle para o chamador. A goto instrução transfere o controle para uma instrução marcada por um rótulo.

Para obter informações sobre a throw instrução que gera uma exceção e transfere incondicionalmente o controle também, consulte a seção de throw instrução do artigo instruções de tratamento de exceção .

A instrução break

A break instrução encerra a instrução de iteração delimitado mais próxima (ou seja, for, foreachou whileloop) ou doswitch instrução. A break instrução transfere o controle para a instrução que segue a instrução terminada, se houver.

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.

Em loops aninhados, a instrução break termina apenas o loop mais interno que o contém, como mostra o exemplo a seguir:

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

Quando você usa a switch instrução dentro de um loop, uma break instrução no final de uma seção de comutador transfere o controle apenas para fora da switch instrução. O loop que contém a switch instrução não é afetado, como mostra o exemplo a seguir:

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.

A instrução continue

A continue instrução inicia uma nova iteração da instrução de iteração delimitando mais próxima (ou seja, for, foreach, whileou do loop), como mostra o exemplo a seguir:

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

A instrução return

A return instrução encerra a execução da função na qual ela aparece e retorna o controle e o resultado da função, se houver, para o chamador.

Se um membro da função não computar um valor, você usará a return instrução sem expressão, como mostra o exemplo a seguir:

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

Como mostra o exemplo anterior, normalmente você usa a return instrução sem expressão para encerrar um membro da função mais cedo. Se um membro da função não contiver a return instrução, ele será encerrado após a execução da última instrução.

Se um membro da função calcular um valor, você usará a return instrução com uma expressão, como mostra o exemplo a seguir:

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;
}

Quando a return instrução tem uma expressão, essa expressão deve ser implicitamente conversível para o tipo de retorno de um membro de função, a menos que seja assíncrona. A expressão retornada de uma async função deve ser implicitamente conversível para o argumento type de Task<TResult> ou ValueTask<TResult>, seja qual for o tipo de retorno da função. Se o tipo de retorno de uma async função for Task ou ValueTask, você usará a return instrução sem expressão.

Ref retorna

Por padrão, a return instrução retorna o valor de uma expressão. Você pode retornar uma referência a uma variável. Valores retornados de referência (ou retornos de ref) são valores que um método retorna por referência ao chamador. Ou seja, o chamador pode modificar o valor retornado por um método e essa alteração é refletida no estado do objeto no método chamado. Para fazer isso, use a return instrução com a ref palavra-chave, como mostra o exemplo a seguir:

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

Um valor retornado de referência permite que um método retorne uma referência a uma variável, em vez de um valor, de volta a um chamador. Em seguida, o chamador pode optar por tratar a variável retornada como se tivesse sido retornada por valor ou por referência. O chamador pode criar uma nova variável que é uma referência ao valor retornado, chamada ref local. Um valor retornado de referência significa que um método retorna uma referência (ou um alias) para alguma variável. O escopo dessa variável deve incluir o método. O tempo de vida dessa variável deve se estender além do retorno do método. As modificações no valor retornado do método pelo chamador são feitas à variável retornada pelo método.

Declarar que um método retorna um valor de retorno de referência indica que o método retorna um alias para uma variável. A intenção de design geralmente é que chamar código acessa essa variável por meio do alias, inclusive para modificá-la. Os métodos retornados por referência não podem ter o tipo voidde retorno.

Para que o chamador modifique o estado do objeto, o valor retornado de referência deve ser armazenado em uma variável definida explicitamente como uma variável de referência.

O ref valor retornado é um alias para outra variável no escopo do método chamado. Você pode interpretar qualquer uso do retorno de ref como usando a variável que ele aliases:

  • Ao atribuir seu valor, você está atribuindo um valor à variável que ele aliases.
  • Ao ler seu valor, você está lendo o valor da variável que ele aliases.
  • Se você retornar por referência, retornará um alias para a mesma variável.
  • Se você passá-lo para outro método por referência, você está passando uma referência para a variável que ele aliases.
  • Quando você faz um alias local ref , você faz um novo alias para a mesma variável.

Um retorno de ref deve ser um contexto ref-safe para o método de chamada. Isso significa:

  • O valor retornado deve ter um tempo de vida que se estenda além da execução do método. Em outras palavras, não pode ser uma variável local no método que a retorna. Pode ser uma instância ou um campo estático de uma classe ou pode ser um argumento passado para o método. A tentativa de retornar uma variável local gera o erro do compilador CS8168, "Não é possível retornar 'obj' local por referência porque não é um ref local".
  • O valor retornado não pode ser o literal null. Um método com um retorno de ref pode retornar um alias a uma variável cujo valor é atualmente o null valor (não comprovado) ou um tipo de valor anulável para um tipo de valor.
  • O valor retornado não pode ser uma constante, um membro de enumeração, o valor retornado por valor de uma propriedade ou um método de um class ou struct.

Além disso, os valores retornados de referência não são permitidos em métodos assíncronos. Um método assíncrono pode retornar antes de concluir a execução, enquanto seu valor retornado ainda é desconhecido.

Um método que retorna um valor retornado de referência deve:

  • Inclua a palavra-chave ref na frente do tipo de retorno.
  • Cada instrução return no corpo do método inclui a palavra-chave ref na frente do nome da instância retornada.

O exemplo a seguir mostra um método que atende a essas condições e retorna uma referência a um Person objeto chamado p:

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

Aqui está um exemplo de retorno de ref mais completo, mostrando a assinatura do método e o corpo do método.

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");
}

O método chamado também pode declarar o valor retornado como ref readonly para retornar o valor por referência e impor que o código de chamada não pode modificar o valor retornado. O método de chamada pode evitar copiar o valor retornado armazenando o valor em uma variável de referência local ref readonly .

O exemplo a seguir define uma Book classe que tem dois String campos Title e Author. Ele também define uma BookCollection classe que inclui uma matriz privada de Book objetos. Objetos de livro individuais são retornados por referência chamando seu GetBookByTitle método.


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();
    }
}

Quando o chamador armazena o valor retornado pelo GetBookByTitle método como um local ref, as alterações feitas pelo chamador no valor retornado são refletidas no BookCollection objeto, como mostra o exemplo a seguir.

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

A instrução goto

A goto instrução transfere o controle para uma instrução marcada por um rótulo, como mostra o exemplo a seguir:

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.

Como mostra o exemplo anterior, você pode usar a goto instrução para sair de um loop aninhado.

Dica

Quando você trabalha com loops aninhados, considere refatorar loops separados em métodos separados. Isso pode levar a um código mais simples e legível sem a instrução goto .

Você também pode usar a goto instrução na instrução para transferir o switch controle para uma seção switch com um rótulo de caso constante, como mostra o exemplo a seguir:

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;
    }
}

Dentro da switch instrução, você também pode usar a instrução goto default; para transferir o controle para a seção switch com o default rótulo.

Se um rótulo com o nome fornecido não existir no membro da função atual ou se a goto instrução não estiver dentro do escopo do rótulo, ocorrerá um erro de tempo de compilação. Ou seja, você não pode usar a instrução para transferir o goto controle do membro da função atual ou para qualquer escopo aninhado.

Especificação da linguagem C#

Para obter mais informações, confira as seguintes seções da especificação da linguagem C#:

Consulte também