Jump-instructies - break
, continue
, , return
en 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, for
foreach
, while
of lus) ofswitch
do
instructie. 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, foreach
for
, of while
do
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 void
niet 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
null
waarde zijn. Een methode met een verw-resultaat kan een alias retourneren aan een variabele waarvan de waarde momenteel denull
(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
ofstruct
.
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:
- Neem het trefwoord verw voor het retourtype op.
- Elke retourinstructie in de hoofdtekst van de methode bevat het trefwoord ref vóór de naam van het geretourneerde exemplaar.
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 Author
Title
. 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: