Qual è l'utilizzo dei delegati in C #?
Qual è l'utilizzo dei delegati in C #?
Risposte:
Ora che abbiamo espressioni lambda e metodi anonimi in C #, utilizzo molto di più i delegati. In C # 1, dove era sempre necessario disporre di un metodo separato per implementare la logica, l'utilizzo di un delegato spesso non aveva senso. In questi giorni utilizzo i delegati per:
I delegati sono molto utili per molti scopi.
Uno di questi scopi è utilizzarli per filtrare sequenze di dati. In questo caso useresti un predicato delegato che accetta un argomento e restituisce true o false a seconda dell'implementazione del delegato stesso.
Ecco un esempio sciocco: sono sicuro che puoi estrapolare qualcosa di più utile da questo:
using System;
using System.Linq;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<String> names = new List<String>
{
"Nicole Hare",
"Michael Hare",
"Joe Hare",
"Sammy Hare",
"George Washington",
};
// Here I am passing "inMyFamily" to the "Where" extension method
// on my List<String>. The C# compiler automatically creates
// a delegate instance for me.
IEnumerable<String> myFamily = names.Where(inMyFamily);
foreach (String name in myFamily)
Console.WriteLine(name);
}
static Boolean inMyFamily(String name)
{
return name.EndsWith("Hare");
}
}
static Boolean inMyFamily(String name)
metodo è il delegato. Where accetta un delegato come parametro. Poiché i delegati sono solo puntatori a funzione quando si passa il nome del metodo nel file, .Where(delegate)
diventa il delegato. Poiché inMyFamily restituisce un tipo booleano, in realtà è considerato un predicato. I predicati sono solo delegati che restituiscono valori booleani.
Ho trovato un'altra risposta interessante:
Un collega mi ha appena posto questa domanda: qual è lo scopo dei delegati in .NET? La mia risposta è stata molto breve e che non aveva trovato online: ritardare l'esecuzione di un metodo.
Fonte: LosTechies
Proprio come sta facendo LINQ.
È possibile utilizzare i delegati per dichiarare variabili e parametri di tipo funzione.
Esempio
Considera il modello di "prestito di risorse". Si desidera controllare la creazione e la pulizia di una risorsa, consentendo al contempo al codice client di "prendere in prestito" la risorsa intermedia.
Questo dichiara un tipo di delegato.
public delegate void DataReaderUser( System.Data.IDataReader dataReader );
Qualsiasi metodo corrispondente a questa firma può essere utilizzato per creare un'istanza di un delegato di questo tipo. In C # 2.0, questa operazione può essere eseguita in modo implicito, semplicemente utilizzando il nome del metodo, nonché utilizzando metodi anonimi.
Questo metodo utilizza il tipo come parametro. Nota l'invocazione del delegato.
public class DataProvider
{
protected string _connectionString;
public DataProvider( string psConnectionString )
{
_connectionString = psConnectionString;
}
public void UseReader( string psSELECT, DataReaderUser readerUser )
{
using ( SqlConnection connection = new SqlConnection( _connectionString ) )
try
{
SqlCommand command = new SqlCommand( psSELECT, connection );
connection.Open();
SqlDataReader reader = command.ExecuteReader();
while ( reader.Read() )
readerUser( reader ); // the delegate is invoked
}
catch ( System.Exception ex )
{
// handle exception
throw ex;
}
}
}
La funzione può essere chiamata con un metodo anonimo come segue. Si noti che il metodo anonimo può utilizzare variabili dichiarate al di fuori di se stesso. Questo è estremamente utile (sebbene l'esempio sia un po 'artificioso).
string sTableName = "test";
string sQuery = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='" + sTableName + "'";
DataProvider.UseReader( sQuery,
delegate( System.Data.IDataReader reader )
{
Console.WriteLine( sTableName + "." + reader[0] );
} );
I delegati possono spesso essere utilizzati al posto di un'interfaccia con un metodo, un esempio comune di questo sarebbe il modello di osservazione. In altre lingue, se desideri ricevere una notifica che è successo qualcosa, potresti definire qualcosa come:
class IObserver{ void Notify(...); }
In C # questo è più comunemente espresso usando eventi, dove il gestore è un delegato, ad esempio:
myObject.SomeEvent += delegate{ Console.WriteLine("..."); };
Un altro ottimo posto per usare i delegati se quando devi passare un predicato in una funzione, ad esempio quando selezioni un set di elementi da un elenco:
myList.Where(i => i > 10);
Quanto sopra è un esempio della sintassi lambda, che potrebbe anche essere stata scritta come segue:
myList.Where(delegate(int i){ return i > 10; });
Un altro punto in cui può essere utile utilizzare i delegati è registrare le funzioni di fabbrica, ad esempio:
myFactory.RegisterFactory(Widgets.Foo, () => new FooWidget());
var widget = myFactory.BuildWidget(Widgets.Foo);
Spero che aiuti!
Sto arrivando su questo molto tardi, ma ho avuto problemi a capire lo scopo dei delegati oggi e ho scritto due semplici programmi che danno lo stesso output che penso spieghi bene il loro scopo.
NoDelegates.cs
using System;
public class Test {
public const int MAX_VALUE = 255;
public const int MIN_VALUE = 10;
public static void checkInt(int a) {
Console.Write("checkInt result of {0}: ", a);
if (a < MAX_VALUE && a > MIN_VALUE)
Console.WriteLine("max and min value is valid");
else
Console.WriteLine("max and min value is not valid");
}
public static void checkMax(int a) {
Console.Write("checkMax result of {0}: ", a);
if (a < MAX_VALUE)
Console.WriteLine("max value is valid");
else
Console.WriteLine("max value is not valid");
}
public static void checkMin(int a) {
Console.Write("checkMin result of {0}: ", a);
if (a > MIN_VALUE)
Console.WriteLine("min value is valid");
else
Console.WriteLine("min value is not valid");
Console.WriteLine("");
}
}
public class Driver {
public static void Main(string [] args) {
Test.checkInt(1);
Test.checkMax(1);
Test.checkMin(1);
Test.checkInt(10);
Test.checkMax(10);
Test.checkMin(10);
Test.checkInt(20);
Test.checkMax(20);
Test.checkMin(20);
Test.checkInt(30);
Test.checkMax(30);
Test.checkMin(30);
Test.checkInt(254);
Test.checkMax(254);
Test.checkMin(254);
Test.checkInt(255);
Test.checkMax(255);
Test.checkMin(255);
Test.checkInt(256);
Test.checkMax(256);
Test.checkMin(256);
}
}
Delegates.cs
using System;
public delegate void Valid(int a);
public class Test {
public const int MAX_VALUE = 255;
public const int MIN_VALUE = 10;
public static void checkInt(int a) {
Console.Write("checkInt result of {0}: ", a);
if (a < MAX_VALUE && a > MIN_VALUE)
Console.WriteLine("max and min value is valid");
else
Console.WriteLine("max and min value is not valid");
}
public static void checkMax(int a) {
Console.Write("checkMax result of {0}: ", a);
if (a < MAX_VALUE)
Console.WriteLine("max value is valid");
else
Console.WriteLine("max value is not valid");
}
public static void checkMin(int a) {
Console.Write("checkMin result of {0}: ", a);
if (a > MIN_VALUE)
Console.WriteLine("min value is valid");
else
Console.WriteLine("min value is not valid");
Console.WriteLine("");
}
}
public class Driver {
public static void Main(string [] args) {
Valid v1 = new Valid(Test.checkInt);
v1 += new Valid(Test.checkMax);
v1 += new Valid(Test.checkMin);
v1(1);
v1(10);
v1(20);
v1(30);
v1(254);
v1(255);
v1(256);
}
}
Un uso leggermente diverso è quello di accelerare la riflessione; ad esempio, invece di usare la reflection ogni volta, puoi usare Delegate.CreateDelegate
per creare un delegato (digitato) per un metodo (a MethodInfo
) e chiamare invece quel delegato. Questo è quindi molto più veloce per chiamata, poiché i controlli sono già stati effettuati.
Con Expression
, puoi anche fare lo stesso per creare codice al volo - ad esempio, puoi facilmente creare un Expression
che rappresenta l'operatore + per un tipo scelto in fase di runtime (per fornire supporto operatore per generici, che il linguaggio non fornisce) ; e puoi compilare un Expression
a un delegato digitato - lavoro fatto.
I delegati vengono utilizzati ogni volta che utilizzi gli eventi: questo è il meccanismo con cui funzionano.
Inoltre, i delegati sono molto utili per cose come l'utilizzo di query LINQ. Ad esempio, molte query LINQ richiedono (spesso Func<T,TResult>
) un delegato che può essere utilizzato per il filtro.
Un esempio potrebbe essere quello visto qui . Hai un metodo per elaborare un oggetto che soddisfa determinati requisiti. Tuttavia, si desidera essere in grado di elaborare l'oggetto in più modi. Invece di dover creare metodi separati, puoi semplicemente assegnare un metodo corrispondente che elabora l'oggetto a un delegato e passare il delegato al metodo che seleziona gli oggetti. In questo modo, puoi assegnare metodi diversi a un metodo selettore. Ho cercato di renderlo facilmente comprensibile.
Uso i delegati per comunicare con i thread.
Ad esempio, potrei avere un'app Win Form che scarica un file. L'app avvia un thread di lavoro per eseguire il download (che impedisce il blocco della GUI). Il thread di lavoro utilizza i delegati per inviare messaggi di stato (ad esempio, avanzamento del download) al programma principale, in modo che la GUI possa aggiornare la barra di stato.
Per gestore di eventi
Per passare il metodo nei parametri di un metodo
Eventi, altre operazioni di qualsiasi genere
Ogni volta che vuoi incapsulare il comportamento, ma invocalo in modo uniforme. Gestori di eventi, funzioni di richiamata, ecc. È possibile eseguire operazioni simili utilizzando Interfacce e cast, ma a volte il comportamento non è necessariamente legato a un tipo o un oggetto . A volte hai solo un comportamento che devi incapsulare.
Inizializzazione pigra dei parametri! Oltre a tutte le risposte precedenti (pattern di strategia, pattern di osservatore, ecc.), I delegati consentono di gestire l'inizializzazione pigra dei parametri. Ad esempio, supponiamo di avere una funzione Download () che richiede molto tempo e restituisce un certo DownloadedObject. Questo oggetto viene consumato da un archivio a seconda di determinate condizioni. In genere, dovresti:
storage.Store(conditions, Download(item))
Tuttavia, con i delegati (più precisamente, lambda) è possibile eseguire le operazioni seguenti, modificando la firma del negozio in modo che riceva una condizione e un Func <Item, DownloadedObject> e utilizzarlo in questo modo:
storage.Store(conditions, (item) => Download(item))
Pertanto, l'archiviazione valuterà il delegato solo se necessario, eseguendo il download a seconda delle condizioni.
Utilizzo dei delegati
Il parametro di confronto in In Array.Sort (T [] array, Confronto di confronto), List.Sort (Confronto di confronto), ecc
Per quanto ne so, i delegati possono essere convertiti in puntatori a funzione. Questo rende la vita MOLTO più facile quando si interopererà con codice nativo che accetta puntatori a funzione, poiché possono essere efficacemente orientati agli oggetti, anche se il programmatore originale non aveva previsto che ciò accadesse.
I delegati vengono utilizzati per chiamare un metodo in base al suo riferimento. Per esempio:
delegate void del_(int no1,int no2);
class Math
{
public static void add(int x,int y)
{
Console.WriteLine(x+y);
}
public static void sub(int x,int y)
{
Console.WriteLine(x-y);
}
}
class Program
{
static void Main(string[] args)
{
del_ d1 = new del_(Math.add);
d1(10, 20);
del_ d2 = new del_(Math.sub);
d2(20, 10);
Console.ReadKey();
}
}