Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
System.Delegate e la
Questo articolo illustra le classi in .NET che supportano i delegati e il modo in cui vengono mappati alla delegate
parola chiave .
Definire i tipi di delegato
Si inizierà con la parola chiave "delegate", perché si tratta principalmente di ciò che si userà mentre si lavora con i delegati. Il codice generato dal compilatore quando si usa la delegate
parola chiave verrà mappato alle chiamate al metodo che richiamano i membri delle Delegate classi e MulticastDelegate .
Si definisce un tipo delegato usando la sintassi simile alla definizione di una firma del metodo. È sufficiente aggiungere la delegate
parola chiave alla definizione.
Continuare a usare il metodo List.Sort() come esempio. Il primo passaggio consiste nel creare un tipo per il delegato di confronto:
// From the .NET Core library
// Define the delegate type:
public delegate int Comparison<in T>(T left, T right);
Il compilatore genera una classe, derivata da System.Delegate
che corrisponde alla firma usata (in questo caso, un metodo che restituisce un numero intero e ha due argomenti). Il tipo di tale delegato è Comparison
. Il Comparison
tipo delegato è un tipo generico. Per altre informazioni, vedere Classi e metodi generici.
Si noti che la sintassi può apparire come se dichiara una variabile, ma in realtà dichiara un tipo. È possibile definire tipi delegati all'interno di classi, direttamente all'interno degli spazi dei nomi o anche nello spazio dei nomi globale.
Annotazioni
Non è consigliabile dichiarare i tipi delegati (o altri tipi) direttamente nello spazio dei nomi globale.
Il compilatore genera anche gestori di aggiunta e rimozione per questo nuovo tipo in modo che i client di questa classe possano aggiungere e rimuovere metodi dall'elenco chiamate di un'istanza. Il compilatore impone che la firma del metodo aggiunto o rimosso corrisponda alla firma utilizzata durante la dichiarazione del metodo.
Dichiarare istanze di delegati
Dopo aver definito il delegato, è possibile creare un'istanza di tale tipo. Analogamente a tutte le variabili in C#, non è possibile dichiarare istanze delegate direttamente in uno spazio dei nomi o nello spazio dei nomi globale.
// inside a class definition:
// Declare an instance of that type:
public Comparison<T> comparator;
Il tipo della variabile è Comparison<T>
, il tipo delegato definito in precedenza. Il nome della variabile è comparator
.
Il frammento di codice precedente ha dichiarato una variabile membro all'interno di una classe . È anche possibile dichiarare variabili delegate che sono variabili locali o argomenti ai metodi.
Richiamare i delegati
Per invocare i metodi presenti nell'elenco di invocazione di un delegato, si chiama quel delegato. All'interno del Sort()
metodo, il codice chiamerà il metodo di confronto per determinare quale ordine inserire gli oggetti:
int result = comparator(left, right);
Nella riga precedente il codice richiama il metodo associato al delegato. La variabile viene considerata come un nome di metodo e la si richiama usando la sintassi di chiamata al metodo normale.
Tale riga di codice fa un presupposto non sicuro: non esiste alcuna garanzia che una destinazione sia stata aggiunta al delegato. Se non sono state collegate destinazioni, la riga precedente genererà un'eccezione NullReferenceException
. I termini usati per risolvere questo problema sono più complessi di un semplice controllo null e sono trattati più avanti in questa serie.
Assegnare, aggiungere e rimuovere destinazioni di chiamata
Questo è il modo in cui viene definito un tipo delegato e come vengono dichiarate e richiamate le istanze del delegato.
Gli sviluppatori che vogliono usare il List.Sort()
metodo devono definire un metodo la cui firma corrisponde alla definizione del tipo delegato e assegnarla al delegato usato dal metodo di ordinamento. Questa assegnazione aggiunge il metodo all'elenco chiamate dell'oggetto delegato.
Si supponga di voler ordinare un elenco di stringhe in base alla loro lunghezza. La funzione di confronto potrebbe essere la seguente:
private static int CompareLength(string left, string right) =>
left.Length.CompareTo(right.Length);
Il metodo viene dichiarato come metodo privato. Va bene. È possibile che questo metodo non faccia parte dell'interfaccia pubblica. Può comunque essere usato come metodo di confronto quando è collegato a un delegato. Il codice chiamante avrà questo metodo associato all'elenco di destinazione dell'oggetto delegato e potrà accedervi tramite tale delegato.
Passando quel metodo al metodo List.Sort()
, si crea quella relazione.
phrases.Sort(CompareLength);
Si noti che viene usato il nome del metodo, senza parentesi. Usare il metodo come argomento indica al compilatore di convertire il riferimento al metodo in un riferimento che può essere utilizzato come target di invocazione del delegato e di collegare tale metodo come target di invocazione.
È anche possibile che sia stato esplicito dichiarando una variabile di tipo Comparison<string>
ed eseguendo un'assegnazione:
Comparison<string> comparer = CompareLength;
phrases.Sort(comparer);
Negli usi in cui il metodo usato come destinazione del delegato è un metodo di piccole dimensioni, è comune usare la sintassi delle espressioni lambda per eseguire l'assegnazione:
Comparison<string> comparer = (left, right) => left.Length.CompareTo(right.Length);
phrases.Sort(comparer);
L'uso di espressioni lambda per le destinazioni del delegato è illustrato più avanti in una sezione successiva.
L'esempio Sort() associa in genere un singolo metodo di destinazione al delegato. Tuttavia, gli oggetti delegati supportano elenchi di chiamate che dispongono di più metodi di destinazione collegati a un oggetto delegato.
Classi Delegate e MulticastDelegate
Il supporto linguistico descritto in precedenza fornisce le funzionalità e il supporto necessari in genere per lavorare con i delegati. Queste funzionalità sono basate su due classi nel framework .NET Core: Delegate e MulticastDelegate.
La System.Delegate
classe e la relativa singola sottoclasse diretta, System.MulticastDelegate
, forniscono il supporto del framework per la creazione di delegati, la registrazione di metodi come destinazioni delegato e la chiamata di tutti i metodi registrati come destinazione del delegato.
È interessante notare che le System.Delegate
classi e System.MulticastDelegate
non sono tipi delegati. Forniscono la base per tutti i tipi delegati specifici. Lo stesso processo di progettazione del linguaggio imposto che non è possibile dichiarare una classe che deriva da Delegate
o MulticastDelegate
. Le regole del linguaggio C# lo vietano.
Al contrario, il compilatore C# crea istanze di una classe derivata da MulticastDelegate
quando si usa la parola chiave del linguaggio C# per dichiarare i tipi delegati.
Questa progettazione ha le sue radici nella prima versione di C# e .NET. Un obiettivo del team di progettazione era garantire che il linguaggio garantisse la sicurezza dei tipi quando si usano delegati. Ciò significava garantire che i delegati siano stati richiamati con il tipo corretto e il numero di argomenti. E che qualsiasi tipo restituito sia stato indicato correttamente in fase di compilazione. I delegati facevano parte della versione 1.0 .NET, precedente ai generics.
Il modo migliore per garantire questa sicurezza dei tipi era che il compilatore creasse le classi delegate concrete che rappresentavano la firma del metodo in uso.
Anche se non è possibile creare direttamente classi derivate, si useranno i metodi definiti in queste classi. Andiamo a esaminare i metodi più comuni che utilizzerai quando lavori con i delegati.
La prima e più importante cosa da ricordare è che ogni delegato con cui lavori è derivato da MulticastDelegate
. Un delegato multicast significa che possono essere invocati più metodi target quando si invoca attraverso un delegato. La progettazione originale ha considerato una distinzione tra delegati in cui è possibile associare e richiamare un solo metodo di destinazione e i delegati in cui è possibile associare e richiamare più metodi di destinazione. Tale distinzione si è dimostrata meno utile in pratica rispetto al pensiero originale. Le due classi diverse sono già state create e sono state incluse nel framework dalla versione pubblica iniziale.
I metodi più usati con i delegati sono Invoke()
e BeginInvoke()
/ EndInvoke()
.
Invoke()
richiamerà tutti i metodi associati a una particolare istanza di delegato. Come illustrato in precedenza, in genere si richiamano delegati usando la sintassi della chiamata al metodo nella variabile delegato. Come si vedrà più avanti in questa serie, esistono modelli che funzionano direttamente con questi metodi.
Dopo aver visto la sintassi del linguaggio e le classi che supportano i delegati, esaminiamo come vengono usati, creati e richiamati i delegati fortemente tipizzati.