Instruções de salto - break
, continue
, return
, e goto
As instruções de salto transferem incondicionalmente o controle. A break
instrução encerra a instrução ouswitch
instrução de iteração anexa mais próxima. A continue
instrução inicia uma nova iteração da instrução de iteração anexa 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 lança uma exceção e também transfere o controle incondicionalmente, consulte A throw
seção de instrução do artigo Instruções de tratamento de exceções.
A break
declaração
A break
instrução encerra a instrução de iteração de fechamento mais próxima (ou seja, for
, , foreach
while
, ou do
loop) ou switch
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 break
instrução termina apenas o loop mais interno que a 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 switch transfere o controle somente 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 continue
declaração
A continue
instrução inicia uma nova iteração da instrução de iteração de fechamento mais próxima (ou seja, for
, , foreach
while
, ou 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 return
declaração
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 calcular um valor, use 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, você normalmente usa a return
instrução sem expressão para encerrar um membro da função antecipadamente. Se um membro da função não contiver a instrução, ela será encerrada return
depois que sua última instrução for executada.
Se um membro da função calcular um valor, use 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 da 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> or 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.
Devoluções Ref
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 de retorno de referência (ou retornos 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 de retorno 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. O chamador pode então optar por tratar a variável retornada como se ela fosse retornada por valor ou por referência. O chamador pode criar uma nova variável que é, em si mesma, uma referência ao valor retornado, chamada de ref local. Um valor de retorno de referência significa que um método retorna uma referência (ou um alias) para alguma variável. O âmbito desta variável deve incluir o método. O tempo de vida dessa variável deve prolongar-se para além do retorno do método. Modificações no valor de retorno do método pelo chamador são feitas na variável que é 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 do design geralmente é que o código de chamada acesse essa variável por meio do alias, inclusive para modificá-la. Os métodos que retornam por referência não podem ter o tipo de retorno void
.
Para que o chamador modifique o estado do objeto, o valor de retorno de referência deve ser armazenado em uma variável que é explicitamente definida como uma variável de referência.
O ref
valor de retorno é um alias para outra variável no escopo do método chamado. Você pode interpretar qualquer uso do retorno ref como usando a variável it 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ê retorná-lo por referência, estará retornando um alias para essa mesma variável.
- Se você passá-lo para outro método por referência, estará passando uma referência para a variável que ele aliases.
- Quando você cria um alias local ref, você cria um novo alias para a mesma variável.
Um retorno ref deve ser ref-safe-context para o método de chamada. Isto significa:
- O valor de retorno 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 campo estático de uma classe, ou pode ser um argumento passado para o método. Tentar retornar uma variável local gera erro de compilador CS8168, "Não é possível retornar local 'obj' por referência porque não é um ref local."
- O valor de retorno não pode ser o literal
null
. Um método com um retorno ref pode retornar um alias para uma variável cujo valor é atualmente onull
valor (não instanciado) ou um tipo de valor anulável para um tipo de valor. - O valor de retorno não pode ser uma constante, um membro de enumeração, o valor de retorno por valor de uma propriedade ou um método de um
class
oustruct
.
Além disso, os valores de retorno de referência não são permitidos em métodos assíncronos. Um método assíncrono pode retornar antes de terminar a execução, enquanto seu valor de retorno ainda é desconhecido.
Um método que retorna um valor de retorno 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 satisfaz 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 ref mais completo, mostrando a assinatura 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 de retorno 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 GetBookByTitle
o valor retornado pelo método como um local ref, as alterações que o chamador faz no valor de retorno são refletidas BookCollection
no 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 goto
declaração
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.
Gorjeta
Ao trabalhar com loops aninhados, considere a refatoração de loops separados em métodos separados. Isso pode levar a um código mais simples e legível sem a goto
declaração.
Você também pode usar a instrução na switch
instrução para transferir o goto
controle para uma seção de switch com um rótulo de maiúsculas e minúsculas 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 próprio 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 em tempo de compilação. Ou seja, você não pode usar a instrução para transferir o goto
controle para fora do membro da função atual ou para qualquer escopo aninhado.
Especificação da linguagem C#
Para obter mais informações, consulte as seguintes seções da especificação da linguagem C#: