Metodi in 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# ed è chiamato da Common Language Runtime (CLR) quando viene avviato il programma.
Nota
In questo argomento vengono descritti i metodi denominati. Per informazioni sulle funzioni anonime, vedere Espressioni Lambda.
Firme del metodo
I metodi vengono dichiarati in un class
oggetto , record
o struct
specificando:
- Un livello di accesso facoltativo, ad esempio
public
oprivate
. Il valore predefinito èprivate
. - Modificatori facoltativi, ad esempio
abstract
osealed
. - Il valore restituito o
void
se il metodo non ha alcun valore. - Nome del metodo.
- Tutti i parametri del metodo. I parametri del metodo vengono racchiusi tra parentesi e separati da virgole. Le parentesi vuote indicano che il metodo non richiede parametri.
Queste parti costituiscono la firma del metodo.
Importante
Un tipo restituito di un metodo non fa parte della firma del metodo in caso di overload dei metodi. Fa tuttavia parte della firma del metodo quando si determina la compatibilità tra un delegato e il metodo a cui fa riferimento.
L'esempio seguente definisce una classe denominata Motorcycle
che contiene cinque metodi:
using System;
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 can override the base class implementation.
public virtual int Drive(TimeSpan time, int speed) { /* Method statements here */ return 0; }
// Derived classes must implement this.
public abstract double GetTopSpeed();
}
La Motorcycle
classe include un metodo di overload, Drive
. Due metodi hanno lo stesso nome, ma devono essere differenziati in base ai relativi tipi di parametri.
Chiamata al metodo
I metodi possono essere di istanza o statici. Per chiamare un metodo di istanza è necessario creare un'istanza di un oggetto e chiamare il metodo nell'oggetto; un metodo di istanza agisce sull'istanza e i relativi dati. Si richiama un metodo statico facendo riferimento al nome del tipo a cui appartiene il metodo; i metodi statici non operano sui dati dell'istanza. Se si tenta di chiamare un metodo statico attraverso un'istanza di un oggetto viene generato un errore del compilatore.
La chiamata a un metodo è un'operazione analoga all'accesso a un campo. Dopo il nome dell'oggetto (se si chiama un metodo di istanza) o il nome del tipo (se si chiama un static
metodo), aggiungere un punto, il nome del metodo e le parentesi. Gli argomenti sono elencati tra parentesi e sono separati da virgole.
La definizione del metodo specifica i nomi e i tipi di tutti i parametri obbligatori. Quando il chiamante chiama il metodo, specifica valori concreti, chiamati argomenti, per ogni parametro. Gli argomenti devono essere compatibili con il tipo di parametro, ma il nome dell'argomento, se usato nel codice chiamante, non deve essere uguale al parametro denominato nel metodo . Nell'esempio seguente il metodo Square
include un singolo parametro di tipo int
denominato i. La prima chiamata al metodo passa al metodo Square
una variabile di tipo int
denominata num, la seconda passa una costante numerica e la terza passa un'espressione.
public class SquareExample
{
public static void Main()
{
// Call with an int variable.
int num = 4;
int productA = Square(num);
// Call with an integer literal.
int productB = Square(12);
// Call with an expression that evaluates to int.
int productC = Square(productA * 3);
}
static int Square(int i)
{
// Store input argument in a local variable.
int input = i;
return input * input;
}
}
La forma più comune di chiamata al metodo usa argomenti posizionali; specifica gli argomenti nello stesso ordine dei parametri del metodo. Per questa ragione, i metodi della classe Motorcycle
possono essere chiamati come nell'esempio seguente. La chiamata al metodo Drive
, ad esempio, include due argomenti che corrispondono ai due parametri nella sintassi del metodo. Il primo diventa il valore del parametro miles
, il secondo il valore del parametro speed
.
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 {0}", speed);
}
}
È anche possibile usare argomenti denominati anziché argomenti posizionali quando si richiama un metodo. Quando si usano gli argomenti denominati, si specifica il nome del parametro seguito da due punti (":") e l'argomento. Gli argomenti del metodo possono essere in qualsiasi ordine, a condizione che siano presenti tutti gli argomenti necessari. L'esempio seguente usa gli argomenti denominati per richiamare il metodo TestMotorcycle.Drive
. In questo esempio gli argomenti denominati vengono passati nell'ordine inverso dall'elenco di parametri del metodo.
using System;
class TestMotorcycle : Motorcycle
{
public override int Drive(int miles, int speed)
{
return (int)Math.Round(((double)miles) / speed, 0);
}
public override double GetTopSpeed()
{
return 108.4;
}
static void Main()
{
TestMotorcycle moto = new TestMotorcycle();
moto.StartEngine();
moto.AddGas(15);
var travelTime = moto.Drive(speed: 60, miles: 170);
Console.WriteLine("Travel time: approx. {0} hours", travelTime);
}
}
// The example displays the following output:
// Travel time: approx. 3 hours
È possibile richiamare un metodo usando argomenti posizionali e denominati. Tuttavia, gli argomenti posizionali possono seguire solo gli argomenti denominati quando gli argomenti denominati si trovano nelle posizioni corrette. L'esempio seguente richiama il metodo TestMotorcycle.Drive
dall'esempio precedente usando un argomento posizionale e un argomento denominato.
var travelTime = moto.Drive(170, speed: 55);
Metodi ereditati e sottoposti a override
Oltre ai membri definiti in modo esplicito in un tipo, un tipo eredita i membri definiti nelle relative classi base. Poiché tutti i tipi del sistema di tipi gestiti ereditano direttamente o indirettamente dalla classe Object, tutti i tipi di ereditano i relativi membri, ad esempio Equals(Object), GetType() e ToString(). L'esempio seguente definisce una classe Person
, crea l'istanza di due oggetti Person
e chiama il metodo Person.Equals
per determinare se i due oggetti sono uguali. Il metodo, tuttavia, non è definito nella Person
classe. Viene Equals
ereditato da Object.
using System;
public class Person
{
public String FirstName;
}
public class ClassTypeExample
{
public static void Main()
{
var p1 = new Person();
p1.FirstName = "John";
var p2 = new Person();
p2.FirstName = "John";
Console.WriteLine("p1 = p2: {0}", p1.Equals(p2));
}
}
// The example displays the following output:
// p1 = p2: False
I tipi possono eseguire l'override dei membri ereditati usando la parola chiave override
e specificando un'implementazione per il metodo sottoposto a override. La firma del metodo deve essere la stessa del metodo sottoposto a override. L'esempio seguente è simile a quello precedente, ad eccezione del fatto che viene eseguito l'override del metodo Equals(Object). Viene anche eseguito l'override del metodo GetHashCode() poiché i due metodi devono fornire risultati coerenti.
using System;
public class Person
{
public String FirstName;
public override bool Equals(object obj)
{
var p2 = obj as Person;
if (p2 == null)
return false;
else
return FirstName.Equals(p2.FirstName);
}
public override int GetHashCode()
{
return FirstName.GetHashCode();
}
}
public class Example
{
public static void Main()
{
var p1 = new Person();
p1.FirstName = "John";
var p2 = new Person();
p2.FirstName = "John";
Console.WriteLine("p1 = p2: {0}", p1.Equals(p2));
}
}
// The example displays the following output:
// p1 = p2: True
Passaggio di parametri
I tipi in C# sono tipi di valore o tipi di riferimento. Per un elenco di tipi di valore predefiniti, vedere Tipi. Per impostazione predefinita, i tipi di valore e i tipi di riferimento vengono passati a un metodo per valore.
Passaggio di parametri per valore
Quando un tipo valore viene passato a un metodo per valore, viene passata al metodo una copia dell'oggetto anziché l'oggetto stesso. Di conseguenza, le modifiche all'oggetto nel metodo chiamato non hanno effetto sull'oggetto originale quando il controllo torna al chiamante.
L'esempio seguente passa un tipo di valore a un metodo per valore e il metodo chiamato tenta di modificare il valore del tipo di valore. Definisce una variabile di tipo int
, che è un tipo di valore, ne inizializza il valore a 20 e la passa a un metodo denominato ModifyValue
che modifica il valore della variabile in 30. Quando il metodo viene restituito, tuttavia, il valore della variabile rimane invariato.
using System;
public class ByValueExample
{
public static void Main()
{
int value = 20;
Console.WriteLine("In Main, value = {0}", value);
ModifyValue(value);
Console.WriteLine("Back in Main, value = {0}", value);
}
static void ModifyValue(int i)
{
i = 30;
Console.WriteLine("In ModifyValue, parameter value = {0}", i);
return;
}
}
// The example displays the following output:
// In Main, value = 20
// In ModifyValue, parameter value = 30
// Back in Main, value = 20
Quando viene passato un oggetto di tipo riferimento a un metodo per valore, viene passato un riferimento all'oggetto per valore, ovvero il metodo riceve un argomento che indica la posizione dell'oggetto, ma non l'oggetto stesso. Se si modifica un membro dell'oggetto usando questo riferimento, la modifica si riflette nell'oggetto quando il controllo torna al metodo chiamante. Tuttavia, la sostituzione dell'oggetto passato al metodo non ha effetto sull'oggetto originale quando il controllo torna al chiamante.
L'esempio seguente definisce una classe (che è un tipo riferimento) denominata SampleRefType
. Crea un'istanza di un oggetto SampleRefType
, assegna 44 al campo value
e passa l'oggetto al metodo ModifyObject
. In questo esempio viene eseguita essenzialmente la stessa cosa dell'esempio precedente, che passa un argomento per valore a un metodo. ma, essendo usato un tipo riferimento, il risultato è diverso. La modifica eseguita in ModifyObject
al campo obj.value
cambia anche il campo value
dell'argomento rt
nel metodo Main
in 33, come mostra l'output dell'esempio.
using System;
public class SampleRefType
{
public int value;
}
public class ByRefTypeExample
{
public static void Main()
{
var rt = new SampleRefType();
rt.value = 44;
ModifyObject(rt);
Console.WriteLine(rt.value);
}
static void ModifyObject(SampleRefType obj)
{
obj.value = 33;
}
}
Passaggio di parametri per riferimento
È necessario passare un parametro per riferimento quando si vuole modificare il valore di un argomento in un metodo e questa modifica deve essere applicata quando il controllo torna al metodo chiamante. Per passare un parametro per riferimento, usare la parola chiave ref
o out
. È anche possibile passare un valore per riferimento per evitare la copia, ma comunque impedire modifiche usando la parola chiave in
.
L'esempio seguente è identico a quello precedente, ad eccezione del fatto che il valore viene passato per riferimento al metodo ModifyValue
. Quando il valore del parametro è modificato nel metodo ModifyValue
, la modifica del valore si riflette quando il controllo torna al chiamante.
using System;
public class ByRefExample
{
public static void Main()
{
int value = 20;
Console.WriteLine("In Main, value = {0}", value);
ModifyValue(ref value);
Console.WriteLine("Back in Main, value = {0}", value);
}
static void ModifyValue(ref int i)
{
i = 30;
Console.WriteLine("In ModifyValue, parameter value = {0}", i);
return;
}
}
// The example displays the following output:
// In Main, value = 20
// In ModifyValue, parameter value = 30
// Back in Main, value = 30
Un modello comune che usa parametri di riferimento implica lo scambio dei valori delle variabili. Si passano due variabili a un metodo per riferimento e il metodo scambia il loro contenuto. L'esempio seguente scambia i valori integer.
using System;
public class RefSwapExample
{
static void Main()
{
int i = 2, j = 3;
System.Console.WriteLine("i = {0} j = {1}" , i, j);
Swap(ref i, ref j);
System.Console.WriteLine("i = {0} j = {1}" , i, j);
}
static void Swap(ref int x, ref int y)
{
int temp = x;
x = y;
y = temp;
}
}
// The example displays the following output:
// i = 2 j = 3
// i = 3 j = 2
Il passaggio di un parametro di tipo riferimento consente di modificare il valore del riferimento stesso anziché il valore dei singoli elementi o campi.
Matrici di parametri
In alcuni casi, il requisito che richiede di specificare il numero esatto di argomenti per il metodo è limitativo. Usando la parola chiave params
per indicare che un parametro è una matrice di parametri, si consente la chiamata al metodo con un numero variabile di argomenti. Il parametro contrassegnato con la parola chiave params
deve essere di tipo matrice e deve essere l'ultimo parametro dell'elenco di parametri del metodo.
Un chiamante può quindi richiamare il metodo in uno dei quattro modi seguenti:
- Passando una matrice del tipo appropriato che contiene il numero desiderato di elementi.
- Passando un elenco delimitato da virgole di singoli argomenti del tipo appropriato al metodo.
null
Passando .- Non specificando un argomento nella matrice di parametri.
L'esempio seguente definisce un metodo denominato GetVowels
che restituisce tutte le vocali da una matrice di parametri. Il Main
metodo illustra tutti e quattro i modi per richiamare il metodo. I chiamanti non sono necessari per fornire argomenti per i parametri che includono il params
modificatore. In questo caso, il parametro è una matrice vuota.
using System;
using System.Linq;
class ParamsExample
{
static void Main()
{
string fromArray = GetVowels(new[] { "apple", "banana", "pear" });
Console.WriteLine($"Vowels from array: '{fromArray}'");
string fromMultipleArguments = GetVowels("apple", "banana", "pear");
Console.WriteLine($"Vowels from multiple arguments: '{fromMultipleArguments}'");
string fromNull = GetVowels(null);
Console.WriteLine($"Vowels from null: '{fromNull}'");
string fromNoValue = GetVowels();
Console.WriteLine($"Vowels from no value: '{fromNoValue}'");
}
static string GetVowels(params string[] input)
{
if (input == null || input.Length == 0)
{
return string.Empty;
}
var vowels = new char[] { 'A', 'E', 'I', 'O', 'U' };
return string.Concat(
input.SelectMany(
word => word.Where(letter => vowels.Contains(char.ToUpper(letter)))));
}
}
// The example displays the following output:
// Vowels from array: 'aeaaaea'
// Vowels from multiple arguments: 'aeaaaea'
// Vowels from null: ''
// Vowels from no value: ''
Parametri e argomenti facoltativi
Una definizione di metodo può specificare che i parametri sono necessari o che sono facoltativi. I parametri sono obbligatori per impostazione predefinita. I parametri facoltativi vengono specificati includendo il valore predefinito del parametro nella definizione del metodo. Quando il metodo viene chiamato, se non sono specificati argomenti per un parametro facoltativo, viene usato il valore predefinito.
Il valore predefinito del parametro deve essere assegnato da uno dei tipi di espressioni seguenti:
Una costante, ad esempio una stringa letterale o un numero.
Espressione del modulo
default(SomeType)
, doveSomeType
può essere un tipo di valore o un tipo di riferimento. Se è un tipo di riferimento, è effettivamente lo stesso di specificarenull
. È possibile usare ildefault
valore letterale, poiché il compilatore può dedurre il tipo dalla dichiarazione del parametro.Un'espressione del form
new ValType()
, doveValType
è un tipo di valore. Questo richiama il costruttore implicito senza parametri del tipo di valore, che non è un membro effettivo del tipo.Nota
In C# 10 e versioni successive, quando un'espressione del modulo
new ValType()
richiama il costruttore senza parametri definito in modo esplicito di un tipo di valore, il compilatore genera un errore come valore di parametro predefinito deve essere una costante in fase di compilazione. Usare l'espressionedefault(ValType)
odefault
il valore letterale per specificare il valore del parametro predefinito. Per altre informazioni sui costruttori senza parametri, vedere la sezione Struct inizializzazione e valori predefiniti dell'articolo Tipi di struttura .
Se un metodo include parametri obbligatori e facoltativi, i parametri facoltativi sono definiti alla fine dell'elenco di parametri, dopo tutti i parametri obbligatori.
L'esempio seguente definisce un metodo, ExampleMethod
, con un parametro obbligatorio e due facoltativi.
using System;
public class Options
{
public void ExampleMethod(int required, int optionalInt = default,
string? description = default)
{
var msg = $"{description ?? "N/A"}: {required} + {optionalInt} = {required + optionalInt}";
Console.WriteLine(msg);
}
}
Se viene richiamato un metodo con più argomenti facoltativi usando argomenti posizionali, il chiamante deve specificare un argomento per tutti i parametri facoltativi, dal primo all'ultimo parametro per il quale è specificato un argomento. Nel caso del ExampleMethod
metodo, ad esempio, se il chiamante fornisce un argomento per il parametro, deve anche specificarne uno per il description
optionalInt
parametro. opt.ExampleMethod(2, 2, "Addition of 2 and 2");
è una chiamata a un metodo valido; opt.ExampleMethod(2, , "Addition of 2 and 0");
genera un errore di compilazione "Argomento mancante".
Se un metodo viene chiamato usando argomenti denominati o una combinazione di argomenti posizionali e denominati, il chiamante può omettere tutti gli argomenti successivi all'ultimo argomento posizionale nella chiamata al metodo.
L'esempio seguente esegue una chiamata al metodo ExampleMethod
tre volte. Le prime due chiamate al metodo usano argomenti posizionali. La prima omette entrambi gli argomenti facoltativi, mentre la seconda omette l'ultimo argomento. La terza chiamata al metodo fornisce un argomento posizionale per il description
parametro obbligatorio, ma usa un argomento denominato per specificare un valore al parametro mentre omette l'argomento optionalInt
.
public class OptionsExample
{
public static void Main()
{
var opt = new Options();
opt.ExampleMethod(10);
opt.ExampleMethod(10, 2);
opt.ExampleMethod(12, description: "Addition with zero:");
}
}
// The example displays the following output:
// N/A: 10 + 0 = 10
// N/A: 10 + 2 = 12
// Addition with zero:: 12 + 0 = 12
L'uso di parametri facoltativi ha effetto sulla risoluzione dell'overload o sulla modalità con la quale il compilatore C# determina l'overload specifico da richiamare tramite una chiamata al metodo come descritto di seguito:
- Un metodo, un indicizzatore o un costruttore è un candidato per l'esecuzione se ogni parametro è facoltativo o corrisponde, per nome o per posizione, a un solo argomento nell'istruzione chiamante e tale argomento può essere convertito nel tipo del parametro.
- Se è disponibile più di un candidato, agli argomenti specificati in modo esplicito vengono applicate le regole di risoluzione dell'overload per le conversioni preferite. Gli argomenti omessi per i parametri facoltativi vengono ignorati.
- Se due candidati vengono giudicati ugualmente validi, la preferenza passa a un candidato che non dispone di parametri facoltativi per cui gli argomenti sono stati omessi nella chiamata. Si tratta di una conseguenza di una preferenza generale nella risoluzione dell'overload per i candidati che hanno un numero di parametri inferiore.
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 la return
parola chiave. Un'istruzione con la parola chiave return
seguita da una variabile, una costante o un'espressione corrispondente al tipo restituito restituirà tale valore al chiamante del metodo. Per usare la parola chiave return
per restituire un valore, sono obbligatori metodi con un tipo restituito non void. 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 parola chiave return
, l'esecuzione del metodo verrà interrotta quando verrà raggiunta la fine del blocco di codice.
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 usare un valore restituito da un metodo, il metodo chiamante può usare la chiamata al metodo stessa ovunque è sufficiente un valore dello stesso tipo. È inoltre possibile assegnare il valore restituito a una variabile. I due esempi seguenti di codice ottengono lo stesso risultato:
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 result
, per archiviare un valore è facoltativo. Potrebbe migliorare la leggibilità del codice o potrebbe essere necessario se si desidera archiviare il valore originale dell'argomento per l'intero ambito del metodo.
In alcuni casi, si vuole che il metodo restituisca più di un singolo valore. È possibile farlo facilmente usando i tipi di tupla e i valori letterali tupla. Il tipo di tupla definisce i tipi di dati degli elementi della tupla. I letterali di tupla specificano i valori effettivi della tupla restituita. Nell'esempio seguente (string, string, string, int)
definisce il tipo di tupla restituito dal metodo GetPersonalInfo
. L'espressione (per.FirstName, per.MiddleName, per.LastName, per.Age)
è il letterale della tupla; il metodo restituisce il nome iniziale, centrale e finale e la durata di un oggetto PersonInfo
.
public (string, string, string, int) GetPersonalInfo(string id)
{
PersonInfo per = PersonInfo.RetrieveInfoById(id);
return (per.FirstName, per.MiddleName, per.LastName, per.Age);
}
Il chiamante può quindi usare la tupla restituita con codice simile al seguente:
var person = GetPersonalInfo("111111111");
Console.WriteLine($"{person.Item1} {person.Item3}: age = {person.Item4}");
È anche possibile assegnare i nomi agli elementi della tupla nella definizione del tipo della tupla. L'esempio seguente mostra una versione alternativa del metodo GetPersonalInfo
che usa elementi denominati:
public (string FName, string MName, string LName, int Age) GetPersonalInfo(string id)
{
PersonInfo per = PersonInfo.RetrieveInfoById(id);
return (per.FirstName, per.MiddleName, per.LastName, per.Age);
}
La chiamata precedente al metodo GetPersonalInfo
può quindi essere modificata come segue:
var person = GetPersonalInfo("111111111");
Console.WriteLine($"{person.FName} {person.LName}: age = {person.Age}");
Se un metodo viene passato una matrice come argomento e modifica il valore di singoli elementi, non è necessario che il metodo restituisca la matrice, anche se è possibile scegliere di farlo per un buon stile o flusso funzionale dei valori. L'operazione non è necessaria perché C# passa tutti i tipi riferimento per valore e il valore di un riferimento a una matrice è il puntatore alla matrice. Nell'esempio seguente le modifiche al contenuto della matrice values
eseguite nel metodo DoubleValues
sono rilevabili da qualsiasi codice che include un riferimento alla matrice.
using System;
public class ArrayValueExample
{
static void Main(string[] args)
{
int[] values = { 2, 4, 6, 8 };
DoubleValues(values);
foreach (var value in values)
Console.Write("{0} ", value);
}
public static void DoubleValues(int[] arr)
{
for (int ctr = 0; ctr <= arr.GetUpperBound(0); ctr++)
arr[ctr] = arr[ctr] * 2;
}
}
// The example displays the following output:
// 4 8 12 16
Metodi di estensione
In genere, è possibile aggiungere un metodo a un tipo esistente in due modi:
- Modificare il codice sorgente per il tipo. Non è possibile eseguire questa operazione, naturalmente, se non si possiede il codice sorgente del tipo. Questa operazione diventa una modifica importante se si aggiungono anche tutti i campi dati privati per supportare il metodo.
- Definire il nuovo metodo in una classe derivata. Un metodo non può essere aggiunto in questo modo usando l'ereditarietà per altri tipi, ad esempio strutture ed enumerazioni. Questo modo non può essere usato neanche per "aggiungere" un metodo a una classe sealed.
I metodi di estensione consentono di "aggiungere" un metodo a un tipo esistente senza modificare il tipo o implementare il nuovo metodo in un tipo ereditato. Anche il metodo di estensione non deve risiedere nello stesso assembly del tipo esteso. Un metodo di estensione viene chiamato come se fosse un membro definito di un tipo.
Per altre informazioni, vedere Metodi di estensione.
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 restituisce al chiamante se l'attività attesa non viene completata e lo stato del metodo con la await
parola chiave viene sospeso fino al completamento dell'attività attesa. Una volta completata l'attività, l'esecuzione del metodo può riprendere.
Nota
Un metodo asincrono restituisce al chiamante quando rileva il primo oggetto atteso che non è ancora completo o ottiene alla fine del metodo asincrono, che si verifica prima.
Un metodo asincrono in genere ha un tipo restituito di Task<TResult>, TaskIAsyncEnumerable<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 restituito simile all'attività.
Nell'esempio seguente DelayAsync
è un metodo asincrono con un'istruzione return che restituisce un valore Integer. Poiché è un metodo asincrono, la dichiarazione del metodo deve avere un tipo restituito di Task<int>
. Poiché il tipo restituito è Task<int>
, la valutazione dell'espressione await
in DoSomethingAsync
genera un numero intero come dimostra l'istruzione int result = await delayTask
seguente.
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 in, ref o out, ma può chiamare metodi che hanno tali parametri.
Per altre informazioni sui metodi asincroni, vedere Programmazione asincrona con tipi restituiti asincroni e await eAsync.
Membri con corpo di espressione
È comune avere definizioni di metodo che semplicemente restituiscono immediatamente con il risultato di un'espressione o che hanno un'unica istruzione come corpo del metodo. È disponibile 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 (come per le 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.
Iterators
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 alla volta. Quando viene raggiunta un'istruzione yield return
, viene memorizzata la posizione corrente in modo che il chiamante possa richiedere l'elemento successivo della sequenza.
Il tipo restituito di un iteratore può essere IEnumerable, , IEnumerable<T>IAsyncEnumerable<T>, IEnumerator, o IEnumerator<T>.
Per altre informazioni, vedere Iteratori.