Condividi tramite


Metodi (Guida per programmatori C#)

Un metodo è un blocco di codice che contiene una serie di istruzioni. Un programma fa in modo che le istruzioni vengano eseguite chiamando il metodo e specificando gli argomenti del metodo obbligatori. In C#, ogni istruzione eseguita viene attuata nel contesto di un metodo.

Il Main metodo è il punto di ingresso per ogni applicazione C# e viene chiamato da Common Language Runtime (CLR) all'avvio del programma. In un'applicazione che usa istruzioni di primo livello, il Main metodo viene generato dal compilatore e contiene tutte le istruzioni di primo livello.

Annotazioni

Questo articolo illustra i metodi denominati. Per informazioni sulle funzioni anonime, consultare Espressioni lambda.

Firme dei metodi

I metodi vengono dichiarati in una classe, uno struct o un'interfaccia specificando il livello di accesso, public ad esempio o private, modificatori facoltativi come abstract o sealed, il valore restituito, il nome del metodo e qualsiasi parametro del metodo. Queste parti insieme sono la firma del metodo .

Importante

Il tipo di ritorno di un metodo non fa parte della firma del metodo ai fini del sovraccarico dei metodi. Fa tuttavia parte della firma del metodo quando si determina la compatibilità tra un delegato e il metodo a cui fa riferimento.

I parametri del metodo vengono racchiusi tra parentesi e separati da virgole. Le parentesi vuote indicano che il metodo non richiede parametri. Questa classe contiene quattro metodi:

abstract class Motorcycle
{
    // Anyone can call this.
    public void StartEngine() {/* Method statements here */ }

    // Only derived classes can call this.
    protected void AddGas(int gallons) { /* Method statements here */ }

    // Derived classes can override the base class implementation.
    public virtual int Drive(int miles, int speed) { /* Method statements here */ return 1; }

    // Derived classes must implement this.
    public abstract double GetTopSpeed();
}

Accesso al metodo

La chiamata a un metodo su un oggetto è simile all'accesso a un campo. Dopo il nome dell'oggetto, aggiungere un punto, il nome del metodo e le parentesi. Gli argomenti sono elencati tra parentesi e sono separati da virgole. I metodi della Motorcycle classe possono quindi essere chiamati come nell'esempio seguente:

class TestMotorcycle : Motorcycle
{
    public override double GetTopSpeed()
    {
        return 108.4;
    }

    static void Main()
    {
        TestMotorcycle moto = new TestMotorcycle();

        moto.StartEngine();
        moto.AddGas(15);
        moto.Drive(5, 20);
        double speed = moto.GetTopSpeed();
        Console.WriteLine($"My top speed is {speed}");
    }
}

Parametri del metodo e argomenti

La definizione del metodo specifica i nomi e i tipi di tutti i parametri obbligatori. Quando il codice invocante chiama il metodo, vengono forniti valori concreti denominati argomenti per ciascun parametro. Gli argomenti devono essere compatibili con il tipo di parametro, ma il nome dell'argomento (se presente) usato nel codice chiamante non deve essere uguale al parametro denominato definito nel metodo . Per esempio:

public void Caller()
{
    int numA = 4;
    // Call with an int variable.
    int productA = Square(numA);

    int numB = 32;
    // Call with another int variable.
    int productB = Square(numB);

    // Call with an integer literal.
    int productC = Square(12);

    // Call with an expression that evaluates to int.
    productC = Square(productA * 3);
}

int Square(int i)
{
    // Store input argument in a local variable.
    int input = i;
    return input * input;
}

Passaggio per riferimento e passaggio per valore

Per impostazione predefinita, quando un'istanza di un tipo valore viene passata a un metodo, la copia viene passata anziché l'istanza stessa. Di conseguenza, le modifiche apportate all'argomento non hanno alcun effetto sull'istanza originale nel metodo chiamante. Per passare un'istanza di tipo valore per riferimento, usare la ref parola chiave . Per altre informazioni, vedere Passaggio di parametri Value-Type.

Quando un oggetto di un tipo riferimento viene passato a un metodo, viene passato un riferimento all'oggetto . Ovvero, il metodo riceve non l'oggetto stesso, ma un argomento che indica la posizione dell'oggetto. Se si modifica un membro dell'oggetto utilizzando questo riferimento, la modifica si riflette nell'argomento nel metodo chiamante, anche se si passa l'oggetto per valore.

Per creare un tipo riferimento, usare la class parola chiave , come illustrato nell'esempio seguente:

public class SampleRefType
{
    public int value;
}

Ora, se si passa un oggetto basato su questo tipo a un metodo, viene passato un riferimento all'oggetto . Nell'esempio seguente viene passato un oggetto di tipo SampleRefType al metodo ModifyObject:

public static void TestRefType()
{
    SampleRefType rt = new SampleRefType();
    rt.value = 44;
    ModifyObject(rt);
    Console.WriteLine(rt.value);
}

static void ModifyObject(SampleRefType obj)
{
    obj.value = 33;
}

L'esempio esegue essenzialmente la stessa operazione dell'esempio precedente in quanto passa un argomento per valore a un metodo. Tuttavia, poiché viene usato un tipo riferimento, il risultato è diverso. La modifica apportata al campo value del parametro, obj, cambia anche il campo value dell'argomento, rt, nel metodo TestRefType. Il TestRefType metodo visualizza 33 come output.

Per ulteriori informazioni su come passare i tipi di riferimento per riferimento e per valore, vedere Passaggio di Reference-Type parametri e Tipi di riferimento.

Valori restituiti

I metodi possono restituire un valore al chiamante. Se il tipo restituito (il tipo elencato prima del nome del metodo) non è void, il metodo può restituire il valore usando l'istruzione return. Un'istruzione con la return parola chiave seguita da un valore che corrisponde al tipo restituito restituirà tale valore al chiamante del metodo.

Il valore può essere restituito al chiamante per valore o per riferimento. I valori vengono restituiti al chiamante per riferimento se la ref parola chiave viene usata nella firma del metodo e segue ogni return parola chiave. Ad esempio, la firma del metodo e l'istruzione return seguenti indicano che il metodo restituisce una variabile denominata estDistance per riferimento al chiamante.

public ref double GetEstimatedDistance()
{
    return ref estDistance;
}

La parola chiave return interrompe anche l'esecuzione del metodo. Se il tipo restituito è void, un'istruzione return senza un valore è tuttavia utile per interrompere l'esecuzione del metodo. Senza la return parola chiave , il metodo smetterà di eseguire quando raggiunge la fine del blocco di codice. I metodi con un tipo di ritorno non void devono usare la parola chiave return per restituire un valore. Ad esempio, questi due metodi usano la parola chiave return per restituire numeri interi:

class SimpleMath
{
    public int AddTwoNumbers(int number1, int number2)
    {
        return number1 + number2;
    }

    public int SquareANumber(int number)
    {
        return number * number;
    }
}

Per utilizzare un valore restituito da un metodo, il metodo chiamante può usare la chiamata al metodo stesso in qualsiasi punto in cui un valore dello stesso tipo sia sufficiente. È anche possibile assegnare il valore restituito a una variabile. Ad esempio, i due esempi di codice seguenti hanno lo stesso obiettivo:

int result = obj.AddTwoNumbers(1, 2);
result = obj.SquareANumber(result);
// The result is 9.
Console.WriteLine(result);
result = obj.SquareANumber(obj.AddTwoNumbers(1, 2));
// The result is 9.
Console.WriteLine(result);

L'uso di una variabile locale, in questo caso , resultper archiviare un valore è facoltativo. Può contribuire alla leggibilità del codice oppure potrebbe essere necessario se è necessario archiviare il valore originale dell'argomento per l'intero ambito del metodo.

Per usare un valore restituito dal riferimento da un metodo, è necessario dichiarare una variabile locale ref se si intende modificarne il valore. Ad esempio, se il Planet.GetEstimatedDistance metodo restituisce un Double valore per riferimento, è possibile definirlo come variabile locale ref con codice simile al seguente:

ref double distance = ref Planet.GetEstimatedDistance();

La restituzione di una matrice multidimensionale da un metodo , Mche modifica il contenuto della matrice non è necessaria se la funzione chiamante ha passato la matrice a M. È possibile restituire la matrice risultante da M per uno stile corretto o un flusso funzionale di valori, ma non è necessario perché C# passa tutti i tipi di riferimento per valore e il valore di un riferimento di matrice è il puntatore alla matrice. Nel metodo Mtutte le modifiche apportate al contenuto della matrice sono osservabili da qualsiasi codice con un riferimento alla matrice, come illustrato nell'esempio seguente:

static void Main(string[] args)
{
    int[,] matrix = new int[2, 2];
    FillMatrix(matrix);
    // matrix is now full of -1
}

public static void FillMatrix(int[,] matrix)
{
    for (int i = 0; i < matrix.GetLength(0); i++)
    {
        for (int j = 0; j < matrix.GetLength(1); j++)
        {
            matrix[i, j] = -1;
        }
    }
}

Metodi asincroni

Tramite la funzionalità async, è possibile richiamare i metodi asincroni senza usare callback espliciti o suddividere manualmente il codice in più metodi o espressioni lambda.

Se si contrassegna un metodo con il modificatore async , è possibile usare l'operatore await nel metodo. Quando il controllo raggiunge un'espressione await nel metodo asincrono, il controllo torna al chiamante e l'esecuzione del metodo viene sospesa fino al completamento del task atteso. Una volta completata l'attività, l'esecuzione del metodo può riprendere.

Annotazioni

Un metodo async viene restituito al chiamante quando rileva il primo oggetto atteso che non è ancora completo o raggiunge la fine del metodo async, qualunque si verifichi prima.

Un metodo asincrono ha in genere un tipo restituito di Task<TResult>, Task, IAsyncEnumerable<T> o void. Il tipo restituito void viene usato principalmente per definire i gestori eventi in cui è necessario un tipo restituito void. Un metodo asincrono che restituisce void non può essere atteso e il chiamante di un metodo che restituisce void non può intercettare le eccezioni generate dal metodo. Un metodo asincrono può avere qualsiasi tipo di ritorno simile a un'attività.

Nell'esempio seguente è DelayAsync un metodo asincrono che ha un tipo restituito di Task<TResult>. DelayAsync ha un'istruzione return che restituisce un numero intero. Pertanto, la dichiarazione del metodo di DelayAsync deve avere un tipo restituito di Task<int>. Poiché il tipo restituito è Task<int>, la valutazione dell'espressione await in DoSomethingAsync produce un numero intero come illustrato nell'istruzione seguente: int result = await delayTask.

Il Main metodo è un esempio di metodo asincrono con un tipo restituito di Task. Passa al DoSomethingAsync metodo e, poiché è espresso con una singola riga, può omettere le async parole chiave e await . Poiché DoSomethingAsync è un metodo asincrono, il task per la chiamata a DoSomethingAsync deve essere atteso, come illustrato nell'istruzione seguente: await DoSomethingAsync();.

class Program
{
    static Task Main() => DoSomethingAsync();

    static async Task DoSomethingAsync()
    {
        Task<int> delayTask = DelayAsync();
        int result = await delayTask;

        // The previous two statements may be combined into
        // the following statement.
        //int result = await DelayAsync();

        Console.WriteLine($"Result: {result}");
    }

    static async Task<int> DelayAsync()
    {
        await Task.Delay(100);
        return 5;
    }
}
// Example output:
//   Result: 5

Un metodo asincrono non può dichiarare parametri ref o out , ma può chiamare metodi con tali parametri.

Per ulteriori informazioni sui metodi asincroni, vedere Programmazione asincrona con async e await e Tipi restituiti asincroni.

Definizioni di corpo di espressione

È comune avere definizioni di metodo che restituiscono immediatamente il risultato di un'espressione o che hanno una singola istruzione nel corpo del metodo. Esiste un collegamento alla sintassi per definire tali metodi usando =>:

public Point Move(int dx, int dy) => new Point(x + dx, y + dy);
public void Print() => Console.WriteLine(First + " " + Last);
// Works with operators, properties, and indexers too.
public static Complex operator +(Complex a, Complex b) => a.Add(b);
public string Name => First + " " + Last;
public Customer this[long id] => store.LookupCustomer(id);

Se il metodo restituisce void o è un metodo asincrono, il corpo del metodo deve essere un'espressione di istruzione (uguale a quella delle espressioni lambda). Per le proprietà e gli indicizzatori, devono essere di sola lettura e non si usa la parola chiave della get funzione di accesso.

Iteratori

Un iteratore esegue un'iterazione personalizzata su una raccolta, ad esempio un elenco o una matrice. Un iteratore usa l'istruzione yield return per restituire un elemento per volta. Quando viene raggiunta un'istruzione yield return , viene memorizzata la posizione corrente nel codice. L'esecuzione viene riavviata da tale posizione quando l'iteratore viene chiamato la volta successiva.

Si chiama un iteratore dal codice client usando un'istruzione foreach .

Il tipo restituito di un iteratore può essere IEnumerable, IEnumerable<T>, IAsyncEnumerable<T>, IEnumerator o IEnumerator<T>.

Per altre informazioni, vedere Iteratori.

Specifiche del linguaggio C#

Per ulteriori informazioni, consulta le Specifiche del linguaggio C# . La specifica del linguaggio costituisce il riferimento ufficiale principale per la sintassi e l'uso di C#.

Vedere anche