Instrukcje skoku — break, continue, returni goto

Skok oświadczenia bezwarunkowo kontroli transferu. Instrukcja break kończy najbliższą zamkniętą instrukcję iteracji lub switch instrukcję. Instrukcja continue rozpoczyna nową iterację najbliższej otaczającej instrukcji iteracji. Instrukcja return kończy wykonywanie funkcji, w której jest wyświetlana i zwraca kontrolkę do obiektu wywołującego. Instrukcja goto przenosi kontrolkę do instrukcji oznaczonej etykietą.

Aby uzyskać informacje na temat throw instrukcji, która zgłasza wyjątek i bezwarunkowo przekazuje kontrolę, zobacz throwsekcję Instrukcje instrukcji obsługi wyjątków artykułu.

Instrukcja break

Instrukcja break kończy najbliższą zamkniętą instrukcję iteracji (czyli , for, foreach, , whilelub loop) lubswitchdoinstrukcję. Instrukcja break przenosi kontrolkę do instrukcji, która jest zgodna z instrukcją terminated, jeśli istnieje.

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.

W pętlach break zagnieżdżonych instrukcja kończy tylko najbardziej wewnętrzną pętlę zawierającą pętlę, jak pokazano w poniższym przykładzie:

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

Jeśli używasz instrukcji switch wewnątrz pętli, break instrukcja na końcu sekcji przełącznika przenosi kontrolkę tylko z instrukcji switch . Nie ma to wpływu na pętlę zawierającą instrukcję switch , jak pokazano w poniższym przykładzie:

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.

Instrukcja continue

Instrukcja continue rozpoczyna nową iterację najbliższej otaczającej instrukcji iteracji (czyli pętli , for, foreach, whilelub do ), jak pokazano w poniższym przykładzie:

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

Instrukcja return

Instrukcja return kończy wykonywanie funkcji, w której jest wyświetlana i zwraca kontrolkę oraz wynik funkcji, jeśli istnieje, do obiektu wywołującego.

Jeśli element członkowski funkcji nie oblicza wartości, należy użyć return instrukcji bez wyrażenia, jak pokazano w poniższym przykładzie:

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 pokazano w poprzednim przykładzie, zwykle używasz instrukcji return bez wyrażenia, aby zamknąć element członkowski funkcji na wczesnym etapie. Jeśli element członkowski funkcji nie zawiera instrukcji, kończy się po wykonaniu return ostatniej instrukcji.

Jeśli element członkowski funkcji oblicza wartość, należy użyć return instrukcji z wyrażeniem, jak pokazano w poniższym przykładzie:

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 Gdy instrukcja zawiera wyrażenie, to wyrażenie musi być niejawnie konwertowane na zwracany typ elementu członkowskiego funkcji, chyba że jest asynchroniczne. Wyrażenie zwrócone z async funkcji musi być niejawnie konwertowane na argument Task<TResult> typu lub ValueTask<TResult>, w zależności od tego, który typ jest zwracanym typem funkcji. Jeśli zwracany typ async funkcji to Task lub ValueTask, należy użyć instrukcji return bez wyrażenia.

Wartości zwracane ref

Domyślnie return instrukcja zwraca wartość wyrażenia. Możesz zwrócić odwołanie do zmiennej. Wartości zwracane przez odwołanie (lub zwroty ref) to wartości zwracane przez metodę przy użyciu odwołania do elementu wywołującego. Oznacza to, że obiekt wywołujący może zmodyfikować wartość zwracaną przez metodę, a ta zmiana jest odzwierciedlana w stanie obiektu w wywołanej metodzie. W tym celu użyj instrukcji return ze ref słowem kluczowym, jak pokazano w poniższym przykładzie:

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

Wartość zwracana odwołania umożliwia metodzie zwrócenie odwołania do zmiennej, a nie wartości, z powrotem do elementu wywołującego. Obiekt wywołujący może następnie traktować zwracaną zmienną tak, jakby została zwrócona przez wartość lub przez odwołanie. Obiekt wywołujący może utworzyć nową zmienną, która sama jest odwołaniem do zwracanej wartości, nazywanej lokalną ref. Wartość zwracana odwołania oznacza, że metoda zwraca odwołanie (lub alias) do określonej zmiennej. Zakres tej zmiennej musi zawierać metodę . Okres istnienia tej zmiennej musi wykraczać poza zwrot metody. Modyfikacje wartości zwracanej metody przez obiekt wywołujący są wprowadzane do zmiennej zwracanej przez metodę .

Deklarowanie, że metoda zwraca wartość zwracaną przez odwołanie wskazuje, że metoda zwraca alias do zmiennej. Intencją projektu jest często wywoływanie kodu, które uzyskuje dostęp do tej zmiennej za pośrednictwem aliasu, w tym do modyfikowania go. Metody zwracane przez odwołanie nie mogą mieć zwracanego typu void.

Aby obiekt wywołujący zmodyfikował stan obiektu, wartość zwracana odwołania musi być przechowywana w zmiennej jawnie zdefiniowanej jako zmienna referencyjna.

Wartość ref zwracana jest aliasem do innej zmiennej w zakresie wywoływanej metody. Możesz interpretować dowolne użycie zwrotu odwołania jako przy użyciu zmiennej, która jest aliasem:

  • Po przypisaniu jej wartości przypisujesz wartość do zmiennej, która jest aliasem.
  • Podczas odczytywania jej wartości odczytujesz wartość zmiennej, którą aliasuje.
  • Jeśli zwracasz go przy użyciu odwołania, zwracasz alias do tej samej zmiennej.
  • Jeśli przekazujesz ją do innej metody przy użyciu odwołania, przekazujesz odwołanie do zmiennej, która jest aliasem.
  • Podczas tworzenia aliasu lokalnego ref należy utworzyć nowy alias dla tej samej zmiennej.

Zwracanie odwołania musi być kontekstem ref-safe-context do metody wywołującej. Oznacza to:

  • Wartość zwracana musi mieć okres istnienia, który wykracza poza wykonywanie metody. Innymi słowy, nie może być zmienną lokalną w metodzie zwracającej ją. Może to być wystąpienie lub pole statyczne klasy lub może być argumentem przekazywanym do metody. Próba zwrócenia zmiennej lokalnej powoduje wygenerowanie błędu kompilatora CS8168" "Nie można zwrócić lokalnego "obj" przez odwołanie, ponieważ nie jest to odwołanie lokalne.
  • Wartość zwracana nie może być literałem null. Metoda ze zwracanym odwołaniem może zwrócić alias do zmiennej, której wartość jest obecnie null wartością (bezpodstawną) lub typem wartości null dla typu wartości.
  • Wartość zwracana nie może być stałą, elementem członkowskim wyliczenia, wartością zwracaną przez wartość z właściwości lub metodą klasy class lub struct.

Ponadto wartości zwracane odwołania nie są dozwolone w metodach asynchronicznych. Metoda asynchroniczna może zostać zwrócona przed zakończeniem wykonywania, podczas gdy jej wartość zwracana jest nadal nieznana.

Metoda zwracająca wartość zwracaną przez odwołanie musi:

W poniższym przykładzie przedstawiono metodę spełniającą te warunki i zwracającą odwołanie do Person obiektu o nazwie p:

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

Oto bardziej kompletny przykład zwracania ref, pokazujący zarówno treść podpisu metody, jak i 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");
}

Wywołana metoda może również zadeklarować wartość zwracaną w ref readonly celu zwrócenia wartości przez odwołanie i wymusić, że kod wywołujący nie może zmodyfikować zwróconej wartości. Metoda wywołująca może uniknąć kopiowania zwróconej wartości, przechowując wartość w lokalnej ref readonly zmiennej referencyjnej.

W poniższym przykładzie zdefiniowano klasę zawierającą Book dwa String pola i TitleAuthor. Definiuje również klasę zawierającą prywatną BookCollection tablicę Book obiektów. Poszczególne obiekty książki są zwracane przez odwołanie przez wywołanie metody GetBookByTitle .


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

Gdy obiekt wywołujący przechowuje wartość zwracaną przez GetBookByTitle metodę jako metodę lokalną ref, zmiany wprowadzone przez obiekt wywołujący zwracaną wartość są odzwierciedlane w obiekcie, jak pokazano w BookCollection poniższym przykładzie.

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

Instrukcja goto

Instrukcja goto przenosi kontrolkę do instrukcji oznaczonej etykietą, jak pokazano w poniższym przykładzie:

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 pokazano w poprzednim przykładzie, możesz użyć instrukcji goto , aby wydostać się z zagnieżdżonej pętli.

Napiwek

Podczas pracy z zagnieżdżonymi pętlami rozważ refaktoryzację oddzielnych pętli na oddzielne metody. Może to prowadzić do prostszego, bardziej czytelnego kodu bez instrukcji goto .

Możesz również użyć goto instrukcji w instrukcji switch , aby przenieść kontrolkę do sekcji przełącznika z stałą etykietą wielkości liter, jak pokazano w poniższym przykładzie:

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

W instrukcji switch można również użyć instrukcji goto default; , aby przenieść kontrolkę do sekcji switch z etykietą default .

Jeśli etykieta o podanej nazwie nie istnieje w bieżącym elemencie członkowskim funkcji lub jeśli goto instrukcja nie znajduje się w zakresie etykiety, wystąpi błąd czasu kompilacji. Oznacza to, że nie można użyć instrukcji goto , aby przenieść kontrolę z bieżącego elementu członkowskiego funkcji lub do dowolnego zagnieżdżonego zakresu.

specyfikacja języka C#

Aby uzyskać więcej informacji, zobacz następujące sekcje specyfikacji języka C#:

Zobacz też