Jump-instructies - break, continue, , returnen goto

De jump-instructies dragen onvoorwaardelijke controle over. De break instructie beëindigt de dichtstbijzijnde iteratie-instructie ofswitch-instructie. De continue instructie start een nieuwe iteratie van de dichtstbijzijnde iteratie-instructie. De return instructie beëindigt de uitvoering van de functie waarin deze wordt weergegeven en retourneert het besturingselement aan de aanroeper. De goto instructie draagt het besturingselement over naar een instructie die is gemarkeerd door een label.

Zie de throw instructiesectie van het artikel Met uitzonderingsinstructies voor meer informatie over de throw instructie die een uitzondering genereert en onvoorwaardelijke controle overdraagt.

De break instructie

De break instructie beëindigt de dichtstbijzijnde iteratie-instructie (dat wil zeggen, forforeach, whileof lus) ofswitchdoinstructie. De break instructie draagt het besturingselement over naar de instructie die volgt op de beëindigde instructie, indien van toepassing.

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.

In geneste lussen beëindigt de break instructie alleen de binnenste lus die deze bevat, zoals in het volgende voorbeeld wordt weergegeven:

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

Wanneer u de switch instructie binnen een lus gebruikt, wordt met een break instructie aan het einde van een switchsectie alleen het besturingselement buiten de switch instructie overgedragen. De lus die de switch instructie bevat, wordt niet beïnvloed, zoals in het volgende voorbeeld wordt weergegeven:

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.

De continue instructie

De continue instructie start een nieuwe iteratie van de dichtstbijzijnde iteratie-instructie (dat wil zeggen, foreachfor, of whiledo lus), zoals in het volgende voorbeeld wordt weergegeven:

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

De return instructie

De return instructie beëindigt de uitvoering van de functie waarin deze wordt weergegeven en retourneert het besturingselement en het resultaat van de functie, indien van toepassing, aan de aanroeper.

Als een functielid geen waarde berekent, gebruikt u de return instructie zonder expressie, zoals in het volgende voorbeeld wordt weergegeven:

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

Zoals in het voorgaande voorbeeld wordt weergegeven, gebruikt u doorgaans de return instructie zonder expressie om een functielid vroegtijdig te beëindigen. Als een functielid de return instructie niet bevat, wordt deze beëindigd nadat de laatste instructie is uitgevoerd.

Als een functielid een waarde berekent, gebruikt u de return instructie met een expressie, zoals in het volgende voorbeeld wordt weergegeven:

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

Wanneer de return instructie een expressie heeft, moet die expressie impliciet worden omgezet in het retourtype van een functielid, tenzij deze asynchroon is. De expressie die wordt geretourneerd van een async functie, moet impliciet worden omgezet in het typeargument of ValueTask<TResult>, afhankelijk van Task<TResult> het retourtype van de functie. Als het retourtype van een async functie is Task of ValueTask, gebruikt u de return instructie zonder expressie.

Verw retourneert

Standaard retourneert de return instructie de waarde van een expressie. U kunt een verwijzing naar een variabele retourneren. Retourwaarden voor verwijzingen (of verw-retourneert) zijn waarden die een methode retourneert op basis van een verwijzing naar de aanroeper. Dat wil gezegd, de aanroeper kan de waarde wijzigen die wordt geretourneerd door een methode en die wijziging wordt weerspiegeld in de status van het object in de aangeroepen methode. Gebruik hiervoor de return instructie met het ref trefwoord, zoals in het volgende voorbeeld wordt weergegeven:

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

Met een retourwaarde voor verwijzingen kan een methode een verwijzing naar een variabele retourneren, in plaats van een waarde, terug naar een aanroeper. De aanroeper kan er vervolgens voor kiezen om de geretourneerde variabele te behandelen alsof deze wordt geretourneerd op waarde of op verwijzing. De aanroeper kan een nieuwe variabele maken die zelf een verwijzing is naar de geretourneerde waarde, een lokale verwijzing genoemd. Een retourwaarde voor een verwijzing betekent dat een methode een verwijzing (of een alias) retourneert naar een bepaalde variabele. Het bereik van die variabele moet de methode bevatten. De levensduur van die variabele moet langer duren dan het rendement van de methode. Wijzigingen in de retourwaarde van de methode door de aanroeper worden aangebracht in de variabele die door de methode wordt geretourneerd.

Declaratie dat een methode een retourwaarde retourneert, geeft aan dat de methode een alias retourneert naar een variabele. De ontwerpintentie is vaak dat het aanroepen van code toegang heeft tot die variabele via de alias, inclusief om deze te wijzigen. Methoden die per verwijzing worden geretourneerd, kunnen het retourtype voidniet hebben.

Om de aanroeper de status van het object te kunnen wijzigen, moet de retourwaarde van de verwijzing worden opgeslagen in een variabele die expliciet is gedefinieerd als een referentievariabele.

De ref retourwaarde is een alias naar een andere variabele in het bereik van de aangeroepen methode. U kunt elk gebruik van de verw-retour interpreteren met behulp van de variabele die deze aliassen gebruikt:

  • Wanneer u de waarde toewijst, wijst u een waarde toe aan de variabele die deze aliassen gebruikt.
  • Wanneer u de waarde leest, leest u de waarde van de variabele die wordt aliassen.
  • Als u deze retourneert per verwijzing, retourneert u een alias naar dezelfde variabele.
  • Als u deze op basis van verwijzing doorgeeft aan een andere methode, geeft u een verwijzing door naar de variabele die met deze alias wordt gebruikt.
  • Wanneer u een lokale ref-alias maakt, maakt u een nieuwe alias voor dezelfde variabele.

Een ref-return moet ref-safe-context zijn voor de aanroepmethode. Dat betekent:

  • De retourwaarde moet een levensduur hebben die langer is dan de uitvoering van de methode. Met andere woorden, het kan geen lokale variabele zijn in de methode die deze retourneert. Het kan een exemplaar of statisch veld van een klasse zijn, of het kan een argument zijn dat aan de methode wordt doorgegeven. Als u probeert een lokale variabele te retourneren, wordt de compilerfout CS8168 gegenereerd. 'Kan geen lokale obj retourneren' omdat deze geen ref local is.
  • De retourwaarde kan niet de letterlijke nullwaarde zijn. Een methode met een verw-resultaat kan een alias retourneren aan een variabele waarvan de waarde momenteel de null (niet-geïnstantieerde) waarde is of een null-waardetype voor een waardetype.
  • De retourwaarde kan geen constante zijn, een opsommingslid, de by-value-retourwaarde van een eigenschap of een methode van een class of struct.

Bovendien zijn referentie-retourwaarden niet toegestaan voor asynchrone methoden. Een asynchrone methode kan worden geretourneerd voordat de uitvoering is voltooid, terwijl de retourwaarde nog steeds onbekend is.

Een methode die een retourwaarde retourneert, moet:

In het volgende voorbeeld ziet u een methode die aan deze voorwaarden voldoet en een verwijzing naar een Person object met de naam retourneert p:

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

Hier volgt een completer voorbeeld van een ref-return, met zowel de handtekening van de methode als de hoofdtekst van de methode.

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

De aangeroepen methode kan ook de geretourneerde waarde ref readonly declareren om de waarde op basis van verwijzing te retourneren en afdwingen dat de aanroepende code de geretourneerde waarde niet kan wijzigen. De aanroepmethode kan voorkomen dat de geretourneerde waarde wordt gekopieerd door de waarde op te slaan in een lokale referentievariabele ref readonly .

In het volgende voorbeeld wordt een Book klasse gedefinieerd met twee String velden en AuthorTitle . Er wordt ook een BookCollection klasse gedefinieerd die een privématrix met Book objecten bevat. Afzonderlijke boekobjecten worden naslaginformatie geretourneerd door de methode aan GetBookByTitle te roepen.


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

Wanneer de aanroeper de waarde opslaat die door de GetBookByTitle methode wordt geretourneerd als een lokale verwijzing, worden wijzigingen die de aanroeper aanbrengt in de retourwaarde weergegeven in het BookCollection object, zoals in het volgende voorbeeld wordt weergegeven.

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

De goto instructie

De goto instructie draagt het besturingselement over naar een instructie die is gemarkeerd door een label, zoals in het volgende voorbeeld wordt weergegeven:

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.

Zoals in het voorgaande voorbeeld wordt weergegeven, kunt u de goto instructie gebruiken om een geneste lus te verlaten.

Tip

Wanneer u met geneste lussen werkt, kunt u overwegen om afzonderlijke lussen in afzonderlijke methoden te herstructureren. Dit kan leiden tot een eenvoudigere, beter leesbare code zonder de goto instructie.

U kunt de goto instructie in de switch instructie ook gebruiken om het besturingselement over te dragen naar een switchsectie met een constant caselabel, zoals in het volgende voorbeeld wordt weergegeven:

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

In de switch instructie kunt u ook de instructie goto default; gebruiken om het besturingselement met het default label over te dragen naar de switchsectie.

Als er geen label met de opgegeven naam bestaat in het huidige functielid of als de goto instructie zich niet binnen het bereik van het label bevindt, treedt er een compilatiefout op. Dat wil zeggen dat u de instructie niet kunt gebruiken om het goto beheer over te dragen van het huidige functielid of in een genest bereik.

C#-taalspecificatie

Zie de volgende secties van de C#-taalspecificatie voor meer informatie:

Zie ook