Ma cosa sa veramente? Passando a un delegato, come un callback, un gestore di eventi, un metodo factory, ecc. Tutto ciò che è noto al codice che ottiene questo riferimento delegato è che ha un riferimento a qualcosa che può o dovrebbe chiamare in un momento specifico, o per uno scopo specifico. Non deve sapere esattamente cosa farà quella chiamata (al di fuori di qualsiasi valore di ritorno previsto), dove il codice effettivo che eseguirà le vite, come è stato scelto per essere utilizzato come delegato, ecc. Non ha bisogno di conoscenza a riguardo delegare diverso da quello che ha e sa come chiamarlo, che sono i requisiti minimi (almeno in C #).
La definizione del delegato è quindi poco più di una mini-interfaccia; si definisce un tipo di delegato, possibilmente denominato per il suo scopo apparente nel codice che consuma, che si aspetta un certo numero di parametri con nome e si aspetta a sua volta di restituire un valore particolare. Ciò è ulteriormente semplificato con i tipi di delegati generici incorporati di .NET 3.5 come Action, Func, Predicate, Comparison etc; non è nemmeno necessario definire il proprio delegato strongmente tipizzato, basta chiedere un Func<string, string>
(il lato negativo è che i generici generici sono, beh, generici e generici, non è possibile utilizzare il nome del tipo delegato o nomi di parametri per auto-documentare ciò che il delegato dovrebbe fare per il codice chiamante, o che tipo di cose dovresti dargli).
È simile, quindi, a una definizione dell'interfaccia completa, che definisce un elenco di tali metodi, per nome, con firme completamente definite, che devono essere tutte disponibili in una classe di implementazione. In Java, questo è più o meno come si ottiene la stessa funzionalità che si ottiene con i delegati .NET; un'interfaccia viene definita con un singolo metodo che rappresenta il metodo delegate, le classi che espongono questo metodo per l'utilizzo come delegato sono contrassegnate come implementazione dell'interfaccia e viene fornito un riferimento alla classe, su cui è possibile chiamare il metodo. I delegati semplicemente rimuovono l'intermediario; non ti interessa più che la classe che contiene il metodo implementa qualsiasi interfaccia definita, ti preoccupi che il metodo effettivo in questione corrisponda alla firma richiesta (qui, la firma è solo elenco dei parametri e tipo di ritorno, non incluso il nome del metodo). La classe che contiene quel metodo potrebbe quindi avere una dozzina di metodi con gli stessi parametri e il tipo di ritorno, ma nomi diversi, e una lavorazione che il tuo codice non deve preoccuparsi può scegliere uno specifico da darti come delegato. p>
Sappi che mentre C # è considerato un linguaggio orientato agli oggetti, è anche un linguaggio multi-paradigma, che incorpora aspetti procedurali e funzionali di altri linguaggi in se stesso su richiesta o necessità dimostrata. I delegati sono stati una lezione imparata osservando i programmatori Java e il loro travaglio di configurare semplici applicazioni multi-livello basate su eventi in un paradigma orientato agli oggetti rigorosamente applicato, che richiede centinaia di interfacce a metodo singolo altamente specifiche. Java alla fine ha aggirato questo problema consentendo implementazioni di un'interfaccia anonima, quindi il codice che doveva iniettare un'interfaccia poteva definire un'implementazione una tantum in linea, ma può comunque essere un po 'prolisso. Il rumor è anche in continuo ribaltamento che Oracle ha ceduto alla domanda popolare, e dichiarazioni lambda e altri delegati faranno parte delle prossime specifiche Java 8 (insieme a un sacco di altre qualità di zucchero di sintassi che gli sviluppatori Java stanno salutando dopo aver guardato attraverso recinzione sul lato C #).
C ++ lo ha evitato mantenendo la flessibilità altamente basata sui puntatori del lignaggio C, ma il suo limite era che, a differenza delle interfacce, i puntatori di funzione non potevano auto-documentarsi; tutto quello che potevi sapere, senza documentazione esterna o codice sorgente, è che devi passare l'indirizzo di una funzione, ma non puoi sapere nulla su quali parametri il consumatore proverebbe a passare, cosa potrebbe aspettarsi come valore di ritorno , quale dovrebbe essere la tua funzione da fare e quando potrebbe essere chiamata, ecc. Poiché la capacità di scrivere codice auto-documentante era un principio chiave delle specifiche del linguaggio di C #, i progettisti combinavano i due, consentendo in un certo senso i puntatori di funzione, ma richiede una strong definizione della firma I / O del metodo come tipo, consentendo di puntatori di funzioni auto-descrittivi controllati dal compilatore senza la necessità di interfacce complete.