Quando useresti i delegati in C #? [chiuso]


101

Qual è l'utilizzo dei delegati in C #?


2
Intendi i delegati nel sistema di tipi .NET o la sintassi dei delegati C #? Intendi "quando usi la sintassi del delegato invece della sintassi dell'espressione lambda" o intendi "quando usi i delegati invece di classi / interfacce / metodi virtuali / ecc."?
Niki

Risposte:


100

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:

  • Gestori di eventi (per GUI e altro)
  • Avvio di thread
  • Callback (ad es. Per API asincrone)
  • LINQ e simili (List.Find ecc.)
  • In qualsiasi altro luogo in cui voglio applicare efficacemente il codice "modello" con una logica specializzata all'interno (dove il delegato fornisce la specializzazione)

Vale la pena menzionare il "push" in Push LINQ?
Marc Gravell

3
Non sono sicuro di come lo spiegherei brevemente senza rendere le cose più confuse :) (Probabilmente è coperto dai gestori di eventi, LINQ e comunque dalla parte dei modelli!
Jon Skeet

1
La tua prima frase non ha molto senso.
senfo

3
So cosa stai cercando di dire, ma troverei più facile da leggere quanto segue: "Ora che abbiamo espressioni lambda e metodi anonimi in C #, utilizzo i delegati molto di più". So di essere pignolo, ma ho dovuto leggere quella frase un paio di volte prima che avesse senso per me.
senfo

4
+1 per aver osato sfidare il venerabile Mr Skeet ;-)
indra

29

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");
    }
}

11
Il 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.
Landon Poch

4
"I predicati sono solo delegati che restituiscono valori booleani". +1
daehaai

@LandonPoch quel commento sarebbe stato meglio inserito nella risposta. essendo un principiante non riuscivo a capire dove fosse. Grazie.
Eakan Gopalakrishnan

@ Eakan, non stavo davvero rispondendo alla domanda principale (quando useresti i delegati), quindi l'ho lasciata come commento.
Landon Poch

14

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.


+ 1..non ci avevo pensato in questo modo. Buon punto
Luke101

12

È 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] );
    } );

11

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!


Bei esempi con la sintassi .. Grazie .. :)
Raghu

10

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);
    }
}

5

Un uso leggermente diverso è quello di accelerare la riflessione; ad esempio, invece di usare la reflection ogni volta, puoi usare Delegate.CreateDelegateper 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 Expressionche 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 Expressiona un delegato digitato - lavoro fatto.


5

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.


4

sottoscrizione di gestori di eventi agli eventi


2

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.


1

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.



0

La prima riga di utilizzo è sostituire il pattern Observer / Observable (events). Il secondo, una bella versione elegante del pattern Strategy. Vari altri usi possono essere raccolti, anche se penso più esoterici di questi primi due.


0

Eventi, altre operazioni di qualsiasi genere


0

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.


0

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.


Punto minore, ma "più precisamente, lambda": potresti fare lo stesso con un metodo anonimo in C # 2.0, anche se sarebbe più dettagliato: delegate (ItemType item) {[return] Download (item);}
Marc Gravell

Certo, come LINQ: i lambda non sono altro che zucchero sintattico per i delegati. Hanno solo reso i delegati più accessibili.
Santiago Palladino

I lambda sono leggermente più che semplici delegati, poiché sono convertibili in alberi delle espressioni e delegati.
Jon Skeet

Bene, i lambda possono anche essere compilati in Expressions, che sono completamente diversi dai delegati. Ma il tuo esempio ha usato Func <,>, che può essere usato da un metodo anon. Le espressioni sarebbero estremamente dolorose da scrivere in C # 2.0.
Marc Gravell


0

Il parametro di confronto in In Array.Sort (T [] array, Confronto di confronto), List.Sort (Confronto di confronto), ecc


0

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.


0

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();
        }
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.