Skokové příkazy - break, continue, returna 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 throwv čá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 nebo struct.

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í:

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 TitleAuthor. 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#:

Viz také