Skokové příkazy - break
, continue
, return
a goto
Příkazy jump bezpodmínečně přenesou řízení. Příkaz break
ukončí nejbližší uzavřený příkaz nebo switch
příkaz iterace. Příkaz continue
spustí novou iteraci nejbližšího uzavřeného příkazu iterace. Příkaz return
ukončí provádění funkce, ve které se zobrazí, a vrátí řízení volajícímu. Příkaz goto
přenese řízení na příkaz, který je označen popiskem.
Informace o throw
příkazu, který vyvolá výjimku a bezpodmínečně přenáší kontrolu, naleznete throw
v části Příkazyv článku o příkazech zpracování výjimek.
Příkaz break
Příkaz break
ukončí nejbližší uzavřený iterační příkaz (tj for
. , foreach
, , while
, nebo do
smyčku) nebo switch
příkaz. Příkaz break
přenese řízení na příkaz, který následuje za ukončeným příkazem, pokud existuje.
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.
Ve vnořených smyček příkaz break
ukončí pouze vnitřní smyčku, která ji obsahuje, jak ukazuje následující příklad:
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
Když příkaz použijete switch
uvnitř smyčky, break
příkaz na konci oddílu přepínače přenese řízení pouze z switch
příkazu. Smyčka, která obsahuje switch
příkaz, nemá vliv, jak ukazuje následující příklad:
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.
Příkaz continue
Příkaz continue
spustí novou iteraci nejbližšího uzavřeného příkazu iterace (to znamená , for
, foreach
, while
, nebo do
smyčka), jak ukazuje následující příklad:
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
Příkaz return
Příkaz return
ukončí provádění funkce, ve které se zobrazí, a vrátí řízení a výsledek funkce, pokud existuje, volajícímu.
Pokud člen funkce nevypočítá hodnotu, použijete příkaz bez výrazu return
, jak ukazuje následující příklad:
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
Jak ukazuje předchozí příklad, obvykle použijete return
příkaz bez výrazu k předčasnému ukončení člena funkce. Pokud člen funkce neobsahuje return
příkaz, ukončí se po provedení posledního příkazu.
Pokud člen funkce vypočítá hodnotu, použijete příkaz return
s výrazem, jak ukazuje následující příklad:
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
Pokud příkaz obsahuje výraz, musí být tento výraz implicitně konvertibilní na návratový typ člena funkce, pokud není asynchronní. Výraz vrácený z async
funkce musí být implicitně konvertibilní na typ argumentu Task<TResult> nebo ValueTask<TResult>, podle toho, který je návratový typ funkce. Pokud je návratový async
typ funkce nebo ValueTask, použijete příkaz bez výrazureturn
.Task
Vracení odkazem
Ve výchozím nastavení return
vrátí příkaz hodnotu výrazu. Odkaz na proměnnou můžete vrátit. Návratové hodnoty odkazu (nebo návratové odkazy) jsou hodnoty, které metoda vrací odkazem na volajícího. To znamená, že volající může upravit hodnotu vrácenou metodou a tato změna se projeví ve stavu objektu v volací metodě. Uděláte to tak, že použijete return
příkaz s klíčovým slovem ref
, jak ukazuje následující příklad:
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.");
}
Návratová hodnota odkazu umožňuje metodě vrátit odkaz na proměnnou, nikoli hodnotu zpět volajícímu. Volající se pak může rozhodnout, že bude vrácená proměnná považována za vrácenou hodnotou nebo odkazem. Volající může vytvořit novou proměnnou, která je sama o sobě odkazem na vrácenou hodnotu označovanou jako místní odkaz. Návratová hodnota odkazu znamená, že metoda vrací odkaz (nebo alias) na nějakou proměnnou. Obor této proměnné musí obsahovat metodu. Životnost této proměnné musí být delší než návrat metody. Změny návratové hodnoty metody volajícím jsou provedeny proměnné, která je vrácena metodou.
Deklarování, že metoda vrací návratovou hodnotu odkazu označuje, že metoda vrací alias proměnné. Záměrem návrhu je často to, že volání kódu přistupuje k této proměnné prostřednictvím aliasu, včetně úprav. Metody, které vrací odkaz, nemohou mít návratový typ void
.
Aby volající mohl změnit stav objektu, musí být návratová hodnota odkazu uložena do proměnné, která je explicitně definována jako referenční proměnná.
ref
Návratová hodnota je alias pro jinou proměnnou v oboru volané metody. Použití návratu odkazu můžete interpretovat jako použití proměnné, která aliasy:
- Při přiřazování jeho hodnoty přiřazujete hodnotu proměnné, která aliasy.
- Když přečtete jeho hodnotu, čtete hodnotu proměnné, která aliasy.
- Pokud ho vrátíte pomocí odkazu, vracíte alias do stejné proměnné.
- Pokud ji předáte jiné metodě pomocí odkazu, předáte odkaz na proměnnou, která aliasy.
- Když vytvoříte místní alias odkazu, vytvoříte nový alias pro stejnou proměnnou.
Návrat odkazu musí být odkazově bezpečný kontext volající metody. To znamená:
- Návratová hodnota musí mít životnost, která přesahuje rámec provádění metody. Jinými slovy, nemůže to být místní proměnná v metodě, která ji vrací. Může to být instance nebo statické pole třídy nebo to může být argument předaný metodě. Při pokusu o vrácení místní proměnné se vygeneruje chyba kompilátoru CS8168, nejde vrátit místní obj odkazem, protože se nejedná o odkaz na místní.
- Návratová hodnota nemůže být literál
null
. Metoda s návratem odkazu může vrátit alias proměnné, jejíž hodnota je aktuálněnull
hodnota (neinstantiated) nebo typ hodnoty nullable pro typ hodnoty. - Návratová hodnota nemůže být konstanta, člen výčtu, návratová hodnota podle hodnoty z vlastnosti nebo metoda
class
nebostruct
.
Kromě toho nejsou u asynchronních metod povoleny návratové hodnoty odkazů. Asynchronní metoda se může vrátit před dokončením provádění, zatímco její návratová hodnota je stále neznámá.
Metoda, která vrací návratovou hodnotu odkazu, musí:
- Před návratový typ zahrňte klíčové slovo ref.
- Každý návratový příkaz v těle metody obsahuje klíčové slovo ref před názvem vrácené instance.
Následující příklad ukazuje metodu, která splňuje tyto podmínky a vrací odkaz na Person
objekt s názvem p
:
public ref Person GetContactInformation(string fname, string lname)
{
// ...method implementation...
return ref p;
}
Tady je kompletní příklad vrácení odkazu, který ukazuje jak podpis metody, tak text metody.
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");
}
Volaná metoda může také deklarovat návratovou hodnotu, aby ref readonly
vrátila hodnotu odkazem, a vynutit, aby volající kód nemohl upravit vrácenou hodnotu. Volající metoda se může vyhnout kopírování vrácené hodnoty uložením hodnoty do místní ref readonly
referenční proměnné.
Následující příklad definuje Book
třídu, která má dvě String pole a Title
Author
. Definuje také BookCollection
třídu, která obsahuje privátní pole Book
objektů. Jednotlivé objekty knihy jsou vráceny odkazem voláním jeho GetBookByTitle
metody.
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();
}
}
Když volající uloží hodnotu vrácenou metodou GetBookByTitle
jako místní odkaz, změní, že volající provede návratovou hodnotu, se projeví v objektu BookCollection
, jak ukazuje následující příklad.
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
Příkaz goto
Příkaz goto
přenese řízení na příkaz, který je označen popiskem, jak ukazuje následující příklad:
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.
Jak ukazuje předchozí příklad, můžete pomocí goto
příkazu vyjít z vnořené smyčky.
Tip
Při práci s vnořenými smyčkami zvažte refaktoring samostatných smyček do samostatných metod. To může vést k jednoduššímu čitelnějšímu goto
kódu bez příkazu.
Příkaz v switch
příkazu můžete také použít goto
k přenosu ovládacího prvku do oddílu přepínače s konstantním popiskem případu, jak ukazuje následující příklad:
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;
}
}
V rámci switch
příkazu můžete také použít příkaz goto default;
k přenosu řízení do oddílu default
přepínače s popiskem.
Pokud popisek s daným názvem v aktuálním členu funkce neexistuje nebo pokud goto
příkaz není v rozsahu popisku, dojde k chybě v době kompilace. To znamená, že příkaz nemůžete použít goto
k přenosu kontroly z aktuálního členu funkce nebo do jakéhokoli vnořeného oboru.
specifikace jazyka C#
Další informace najdete v následujících částech specifikace jazyka C#: