Istruzioni Jump - break, continue, returne goto

Le istruzioni jump trasferisce in modo incondizionato il controllo. L'istruzionebreak termina l'istruzione o switch l'istruzione di iterazione più vicina. L'istruzionecontinue avvia una nuova iterazione dell'istruzione iterazione più vicina. L'istruzionereturn termina l'esecuzione della funzione in cui viene visualizzata e restituisce il controllo al chiamante. L'istruzionegoto trasferisce il controllo a un'istruzione contrassegnata da un'etichetta.

Per informazioni sull'istruzione che genera un'eccezione e trasferisce in modo incondizionato anche il controllo, vedere La sezione Istruzione throw dell'articolo throwIstruzioni di gestione delle eccezioni.

Istruzione break

L'istruzione break termina l'istruzione più vicina che racchiude l'istruzione di iterazione, ovvero , forforeachwhileo dociclo.switch L'istruzione break trasferisce il controllo all'istruzione che segue l'istruzione terminata, se presente.

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.

Nei cicli annidati l'istruzione break termina solo il ciclo più interno che lo contiene, come illustrato nell'esempio seguente:

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

Quando si usa l'istruzione switch all'interno di un ciclo, un'istruzione break alla fine di una sezione switch trasferisce il controllo solo all'esterno dell'istruzione switch . Il ciclo che contiene l'istruzione non è interessato, come illustrato nell'esempio switch seguente:

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.

Istruzione continue

L'istruzione continue avvia una nuova iterazione dell'istruzione iterazione più vicina, ovvero , , foreachwhile, o do ciclo, forcome illustrato nell'esempio seguente:

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

Istruzione return

L'istruzione return termina l'esecuzione della funzione in cui viene visualizzata e restituisce il controllo e il risultato della funzione, se presente, al chiamante.

Se un membro della funzione non calcola un valore, usare l'istruzione senza espressione, come illustrato nell'esempio return seguente:

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

Come illustrato nell'esempio precedente, in genere si usa l'istruzione return senza espressione per terminare un membro della funzione in anticipo. Se un membro della funzione non contiene l'istruzione, termina dopo l'esecuzione dell'ultima return istruzione.

Se un membro della funzione calcola un valore, usare l'istruzione con un'espressione, come illustrato nell'esempio return seguente:

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

Quando l'istruzione ha un'espressione, tale return espressione deve essere convertibile in modo implicito nel tipo restituito di un membro di funzione a meno che non sia asincrona. L'espressione restituita da una async funzione deve essere convertibile in modo implicito nell'argomento di tipo di Task<TResult> o ValueTask<TResult>, ovvero il tipo restituito della funzione. Se il tipo restituito di una async funzione è Task o ValueTask, usare l'istruzione return senza espressione.

Valori restituiti

Per impostazione predefinita, l'istruzione return restituisce il valore di un'espressione. È possibile restituire un riferimento a una variabile. A tale scopo, usare l'istruzione con la parola chiave, come illustrato nell'esempio returnrefseguente:

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

I valori restituiti possono essere restituiti dal riferimento (ref restituisce). Un valore restituito di riferimento consente a un metodo di restituire a un chiamante un riferimento a una variabile, invece di un valore. Il chiamante può quindi scegliere di trattare la variabile come se fosse stata restituita da un valore o un riferimento. Il chiamante può creare una nuova variabile che è un riferimento al valore restituito, denominato ref local. Un valore restituito di riferimento indica che un metodo restituisce un riferimento (o alias) a una variabile. L'ambito di tale variabile deve includere il metodo. La durata della variabile deve estendersi oltre la restituzione del controllo del metodo. Le modifiche effettuate dal chiamante al valore restituito del metodo vengono apportate alla variabile restituita dal metodo.

La dichiarazione che un metodo restituisce un valore restituito di riferimento indica che il metodo restituisce un alias a una variabile. La finalità di progettazione è spesso che la chiamata di codice accede alla variabile tramite l'alias, incluso per modificarla. I metodi restituiti da riferimento non possono avere il tipo voidrestituito .

Il ref valore restituito è un alias per un'altra variabile nell'ambito del metodo chiamato. È possibile interpretare qualsiasi uso del valore restituito ref come uso della variabile di cui effettua l'aliasing:

  • Quando si assegna il relativo valore, si assegna un valore alla variabile che esegue l'alias.
  • Quando si legge il valore, si legge il valore della variabile che alias.
  • Se viene restituito in base al riferimento, viene restituito un alias alla stessa variabile.
  • Se lo si passa a un altro metodo per riferimento, si passa un riferimento alla variabile che esegue l'alias.
  • Quando si crea un alias locale ref, si crea un nuovo alias per la stessa variabile.

Una restituzione di riferimento deve essere ref_safe_to_escape al metodo chiamante. Ciò significa che:

  • Il valore restituito deve avere una durata che si estende oltre l'esecuzione del metodo. In altre parole, non può essere una variabile locale nel metodo che lo restituisce. Può essere un'istanza o un campo statico di una classe oppure un argomento passato al metodo. Il tentativo di restituire una variabile locale genera un errore del compilatore CS8168, "Impossibile restituire 'obj' locale in base al riferimento perché non è un riferimento locale di riferimento".
  • Il valore restituito non può essere il valore letterale null. Un metodo con una restituzione ref può restituire un alias a una variabile il cui valore è attualmente il null valore (non valido) o un tipo di valore nullable per un tipo di valore.
  • Il valore restituito non può essere una costante, un membro di enumerazione, il valore restituito per valore da una proprietà o un metodo di o classstruct.

Inoltre, i valori restituiti di riferimento non sono consentiti nei metodi asincroni. Un metodo asincrono può restituire il controllo prima del termine dell'esecuzione, quando il valore restituito è ancora sconosciuto.

Un metodo che restituisce un valore restituito di riferimento deve:

  • Includere la parola chiave ref davanti al tipo restituito.
  • Ogni istruzione return nel corpo del metodo include la parola chiave ref davanti al nome dell'istanza restituita.

L'esempio seguente illustra un metodo che soddisfa le condizioni e restituisce un riferimento a un oggetto Person denominato p:

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

Istruzione goto

L'istruzione goto trasferisce il controllo a un'istruzione contrassegnata da un'etichetta, come illustrato nell'esempio seguente:

var matrices = new Dictionary<string, int[][]>
{
    ["A"] = new[]
    {
        new[] { 1, 2, 3, 4 },
        new[] { 4, 3, 2, 1 }
    },
    ["B"] = new[]
    {
        new[] { 5, 6, 7, 8 },
        new[] { 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.

Come illustrato nell'esempio precedente, è possibile usare l'istruzione goto per uscire da un ciclo annidato.

Suggerimento

Quando si utilizzano cicli annidati, è consigliabile eseguire il refactoring di cicli separati in metodi separati. Ciò può portare a un codice più semplice e leggibile senza l'istruzione goto .

È anche possibile usare l'istruzione nell'istruzione goto per trasferire il controllo in una sezione switch con un'etichetta di maiuscole costanti, come illustrato nell'esempio switch seguente:

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

All'interno dell'istruzione switch è anche possibile usare l'istruzione goto default; per trasferire il controllo alla sezione switch con l'etichetta default .

Se un'etichetta con il nome specificato non esiste nel membro della funzione corrente o se l'istruzione goto non rientra nell'ambito dell'etichetta, si verifica un errore in fase di compilazione. Ovvero, non è possibile usare l'istruzione per trasferire il goto controllo fuori dal membro della funzione corrente o in qualsiasi ambito annidato.

Specifiche del linguaggio C#

Per altre informazioni, vedere le sezioni seguenti delle specifiche del linguaggio C#:

Vedi anche