Jump-instruktioner – break, continue, returnoch goto

Jump-uttrycken överför villkorslöst kontrollen. -instruktionen break avslutar den närmast omslutande iterationssatsen eller switch -instruktionen. -instruktionen continue startar en ny iteration av den närmast omslutande iterationssatsen. Instruktionen return avslutar körningen av funktionen där den visas och returnerar kontrollen till anroparen. -instruktionen goto överför kontrollen till en -instruktion som är markerad med en etikett.

Information om instruktionen throw som genererar ett undantag och ovillkorligt överför kontrollen finns i throw instruktionsavsnittet i artikeln Undantagshanteringsinstruktioner.

Instruktionen break

-instruktionen break avslutar den närmast omslutande iterationssatsen (det vill säga , for, whileforeach, eller do loop) eller switch -instruktionen. Instruktionen break överför kontrollen till instruktionen som följer den avslutade instruktionen, om någon.

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.

I kapslade loopar avslutar instruktionen break endast den innersta loopen som innehåller den, vilket visas i följande exempel:

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

När du använder -instruktionen switch i en loop överförs en break instruktion i slutet av ett switch-avsnitt endast från -instruktionen switch . Loopen som innehåller -instruktionen switch påverkas inte, vilket visas i följande exempel:

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.

Instruktionen continue

-instruktionen continue startar en ny iteration av den närmast omslutande iterationssatsen (det vill säga , forforeach, whileeller do loop), som följande exempel visar:

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

Instruktionen return

Instruktionen return avslutar körningen av funktionen där den visas och returnerar kontrollen och funktionens resultat, om det finns något, till anroparen.

Om en funktionsmedlem inte beräknar ett värde använder du -instruktionen return utan uttryck, som följande exempel visar:

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

Som föregående exempel visar använder du vanligtvis -instruktionen return utan uttryck för att avsluta en funktionsmedlem tidigt. Om en funktionsmedlem inte innehåller -instruktionen avslutas den return efter att dess sista instruktion har körts.

Om en funktionsmedlem beräknar ett värde använder du -instruktionen return med ett uttryck, som följande exempel visar:

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

När instruktionen return har ett uttryck måste uttrycket implicit konverteras till returtypen för en funktionsmedlem om det inte är asynkront. Uttrycket som returneras från en async funktion måste implicit konverteras till typargumentet Task<TResult> för eller ValueTask<TResult>, beroende på vilket som är funktionens returtyp. Om returtypen för en async funktion är Task eller ValueTaskanvänder du -instruktionen return utan uttryck.

Referensreturer

Som standard return returnerar -instruktionen värdet för ett uttryck. Du kan returnera en referens till en variabel. Referensreturvärden (eller referensreturer) är värden som en metod returnerar med referens till anroparen. Det vill säga anroparen kan ändra värdet som returneras av en metod, och den ändringen återspeglas i objektets tillstånd i den anropade metoden. Det gör du genom att använda -instruktionen return med nyckelordet ref , som följande exempel visar:

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

Med ett referensreturvärde kan en metod returnera en referens till en variabel, i stället för ett värde, tillbaka till en anropare. Anroparen kan sedan välja att behandla den returnerade variabeln som om den returnerades med värde eller referens. Anroparen kan skapa en ny variabel som i sig är en referens till det returnerade värdet, som kallas referenslokal. Ett referensreturvärde innebär att en metod returnerar en referens (eller ett alias) till en variabel. Variabelns omfång måste innehålla metoden. Variabelns livslängd måste sträcka sig längre än metodens retur. Ändringar av metodens returvärde av anroparen görs i variabeln som returneras av metoden.

Att deklarera att en metod returnerar ett referensreturvärde anger att metoden returnerar ett alias till en variabel. Design avsikten är ofta att anropa kodåtkomster som variabel via alias, inklusive att ändra den. Metoder som returneras med referens kan inte ha returtypen void.

För att anroparen ska kunna ändra objektets tillstånd måste referensreturvärdet lagras i en variabel som uttryckligen definieras som en referensvariabel.

Returvärdet ref är ett alias till en annan variabel i den anropade metodens omfång. Du kan tolka all användning av referensreturen som att använda variabeln den alias:

  • När du tilldelar dess värde tilldelar du ett värde till variabeln det alias.
  • När du läser dess värde läser du värdet för variabeln det alias.
  • Om du returnerar det med referens returnerar du ett alias till samma variabel.
  • Om du skickar den till en annan metod som referens skickar du en referens till variabeln som den alias.
  • När du skapar ett lokalt referensalias skapar du ett nytt alias för samma variabel.

En referensretur måste vara referenssäker kontext till anropsmetoden. Det innebär:

  • Returvärdet måste ha en livslängd som sträcker sig längre än körningen av metoden. Det kan med andra ord inte vara en lokal variabel i metoden som returnerar den. Det kan vara en instans eller ett statiskt fält i en klass, eller så kan det vara ett argument som skickas till metoden. När du försöker returnera en lokal variabel genereras kompilatorfelet CS8168: "Det går inte att returnera lokala "obj" som referens eftersom det inte är en referenslokal."
  • Returvärdet kan inte vara literalen null. En metod med en referensretur kan returnera ett alias till en variabel vars värde för närvarande null är värdet (oinstantierat) eller en nullbar värdetyp för en värdetyp.
  • Returvärdet kan inte vara en konstant, en uppräkningsmedlem, ett värde för värderetur från en egenskap eller en metod för en class eller struct.

Dessutom tillåts inte referensreturvärden på asynkrona metoder. En asynkron metod kan returneras innan körningen är klar, medan dess returvärde fortfarande är okänt.

En metod som returnerar ett referensreturvärde måste:

  • Inkludera referensnyckelordet framför returtypen.
  • Varje returinstruktur i metodtexten innehåller nyckelordet ref framför namnet på den returnerade instansen.

I följande exempel visas en metod som uppfyller dessa villkor och returnerar en referens till ett Person objekt med namnet p:

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

Här är ett mer komplett referensreturexempel som visar både metodsignaturen och metodtexten.

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

Den anropade metoden kan också deklarera returvärdet för ref readonly att returnera värdet med referens och framtvinga att den anropande koden inte kan ändra det returnerade värdet. Anropande metod kan undvika att kopiera det returnerade värdet genom att lagra värdet i en lokal ref readonly referensvariabel.

I följande exempel definieras en Book klass som har två String fält och TitleAuthor. Den definierar också en BookCollection klass som innehåller en privat matris med Book objekt. Enskilda bokobjekt returneras med referens genom att anropa dess GetBookByTitle metod.


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

När anroparen lagrar det värde som returneras av GetBookByTitle metoden som lokal referens, återspeglas ändringar som anroparen gör i returvärdet i objektet, vilket visas i BookCollection följande exempel.

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

Instruktionen goto

Instruktionen goto överför kontrollen till en -instruktion som är markerad med en etikett, som följande exempel visar:

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.

Som föregående exempel visar kan du använda -instruktionen goto för att komma ut ur en kapslad loop.

Dricks

När du arbetar med kapslade loopar bör du överväga att omstrukturera separata loopar till separata metoder. Det kan leda till en enklare och mer läsbar kod utan -instruktionen goto .

Du kan också använda -instruktionen goto i -instruktionen switch för att överföra kontrollen till ett växelavsnitt med en konstant skiftlägesetikett, som följande exempel visar:

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

I -instruktionen switch kan du också använda -instruktionen goto default; för att överföra kontrollen till switchavsnittet default med etiketten.

Om det inte finns någon etikett med det angivna namnet i den aktuella funktionsmedlemmen, eller om instruktionen goto inte ingår i etikettens omfång, uppstår ett kompileringsfel. Du kan alltså inte använda -instruktionen goto för att överföra kontrollen från den aktuella funktionsmedlemmen eller till något kapslat omfång.

Språkspecifikation för C#

Mer information finns i följande avsnitt i C#-språkspecifikationen:

Se även