Condividi tramite


Istruzioni jump - break, continuereturn, egoto

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 di iterazione racchiusa più vicina. L'istruzionereturn termina l'esecuzione della funzione in cui viene visualizzata e restituisce il controllo al chiamante. L'istruzione trasferisce il goto controllo a un'istruzione contrassegnata da un'etichetta.

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

Istruzione break

L'istruzione break termina l'istruzione o switch l'istruzione di iterazione più vicina, ovvero , forforeachwhile, o do ciclo. 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 switch non è interessato, come illustrato nell'esempio 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 di iterazione racchiusa più vicina ( ovvero , forforeachwhile, , o do ciclo ), come 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 return senza espressione, come illustrato nell'esempio 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 return con un'espressione, come illustrato nell'esempio 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 return ha un'espressione, tale 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>, indipendentemente dal tipo restituito della funzione. Se il tipo restituito di una async funzione è Task o ValueTask, usare l'istruzione return senza espressione.

Restituisce ref

Per impostazione predefinita, l'istruzione return restituisce il valore di un'espressione. È possibile restituire un riferimento a una variabile. I valori restituiti di riferimento (o ref return) sono valori restituiti da un metodo per riferimento al chiamante. Ovvero, il chiamante può modificare il valore restituito da un metodo e tale modifica viene riflessa nello stato dell'oggetto nel metodo chiamato. A tale scopo, usare l'istruzione return con la ref parola chiave , come illustrato nell'esempio seguente:

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

Un valore restituito di riferimento consente a un metodo di restituire un riferimento a una variabile, anziché a un valore, di nuovo a un chiamante. Il chiamante può quindi scegliere di trattare la variabile restituita come se fosse stata restituita per valore o per 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 un alias) a una variabile. L'ambito della variabile deve includere il metodo . La durata della variabile deve estendersi oltre la restituzione del metodo. Le modifiche apportate al valore restituito dal chiamante del metodo vengono apportate alla variabile restituita dal metodo .

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

Affinché il chiamante modifichi lo stato dell'oggetto, il valore restituito del riferimento deve essere archiviato in una variabile definita in modo esplicito come variabile di riferimento.

Il ref valore restituito è un alias di un'altra variabile nell'ambito del metodo chiamato. È possibile interpretare qualsiasi uso del riferimento restituito usando la variabile che esegue l'alias:

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

Una restituzione ref deve essere un contesto di riferimento sicuro per il metodo chiamante. Ciò significa:

  • Il valore restituito deve avere una durata che va 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 può essere un argomento passato al metodo . Il tentativo di restituire una variabile locale genera l'errore del compilatore CS8168, "Non è possibile restituire 'obj' locale per riferimento perché non è un riferimento locale".
  • Il valore restituito non può essere il valore letterale null. Un metodo con un riferimento restituito può restituire un alias a una variabile il cui valore è attualmente il null valore (non giustificato) o un tipo di valore nullable per un tipo 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 dei riferimenti non sono consentiti nei metodi asincroni. Un metodo asincrono può restituire prima del completamento dell'esecuzione, mentre il relativo 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.

Nell'esempio seguente viene illustrato un metodo che soddisfa tali condizioni e restituisce un riferimento a un Person oggetto denominato p:

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

Ecco un esempio di restituzione ref più completo, che mostra sia la firma del metodo che il corpo del metodo.

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

Il metodo chiamato può anche dichiarare il valore restituito come ref readonly per restituire il valore per riferimento e imporre che il codice chiamante non possa modificare il valore restituito. Il metodo chiamante può evitare di copiare il valore restituito archiviando il valore in una variabile di riferimento locale ref readonly .

Nell'esempio seguente viene definita una Book classe con due String campi, Title e Author. Definisce anche una BookCollection classe che include una matrice privata di Book oggetti. I singoli oggetti libro vengono restituiti per riferimento chiamando il relativo GetBookByTitle metodo.


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

Quando il chiamante archivia il valore restituito dal GetBookByTitle metodo come riferimento locale, le modifiche apportate dal chiamante al valore restituito vengono riflesse nell'oggetto, come illustrato nell'esempio BookCollection seguente.

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

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"] =
    [
        [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.

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

Suggerimento

Quando si lavora con cicli annidati, è consigliabile effettuare 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'istruzioneswitchgoto per trasferire il controllo a una sezione switch con un'etichetta case costante, come illustrato nell'esempio 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 è anche possibile usare l'istruzione switchgoto 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. Ciò significa che non è possibile usare l'istruzione goto per trasferire il controllo dal membro della funzione corrente o in qualsiasi ambito annidato.

Specificazione del linguaggio C#

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

Vedere anche