2009-11-14 15 views
15

Tengo problemas para entender cómo funcionan los delegados en C#. Tengo muchos ejemplos de código, pero todavía no pude entenderlo correctamente.Delegados en C#

¿Alguien me puede explicar en "plain english"? ¡Por supuesto! ejemplos de código ayudarán, pero creo que necesito más una descripción de cómo/por qué funciona.

EDIT:

Bueno, la cuestión es: ¿Por qué funciona delegados? ¿Qué es un "diagrama de flujo" de todo el proceso?

¿Cuáles son los requisitos previos para el uso de delegados?

Espero que esto aclare la pregunta.

+1

Puede aclarar: ¿está preguntando sobre cómo usar delegados, o está preguntando cómo C# hace posible a los delegados? – Anton

+0

¡Su pregunta no está clara! ¿Puedes hacerlo más claro? –

Respuesta

19

Una forma de pensar en un delegado es como una referencia a una función. Por ejemplo, supongamos que tiene un botón en una ventana y desea que ocurra algo cuando se hace clic en el botón. Puede adjuntar un delegado al evento Click del botón, y cada vez que el usuario haga clic en este botón, se ejecutará su función.

class MyWindow : Window 
{ 
    Button _button; 

    public MyWindow() 
    { 
     _button = new Button(); 
     // place the button in the window 
     _button.Click += MyWindow.ButtonClicked; 
    } 

    static void ButtonClicked(object sender, RoutedEventArgs e) 
    { 
     MessageBox.Show("Button Clicked"); 
    } 
} 

Observe cómo hago ButtonClicked una función estática - Quiero hacer un punto sobre las funciones no estáticas a continuación.Supongamos en cambio que buttonClicked es un miembro no estático:

class MyWindow : Window 
{ 
    Button _button; 
    int _numClicked = 0; 

    public MyWindow() 
    { 
     this._button = new Button(); 
     // place the button in the window 
     this._button.Click += this.ButtonClicked; 
    } 

    void ButtonClicked(object sender, RoutedEventArgs e) 
    { 
     this._numClicked += 1; 
     MessageBox.Show("Button Clicked " + this._numClicked + " times"); 
    } 
} 

Ahora el delegado contiene una referencia a la función "buttonClicked" y el ejemplo, "este", que se llama al método sobre. La instancia "this" en el constructor MyWindow y "this" en ButtonClicked son iguales.

Este es un caso específico de un concepto conocido como cierres que permite "guardar" el estado - el objeto actual, las variables locales, etc. - al crear un delegado. En el ejemplo anterior, usamos "this" del constructor en el delegado. Podemos hacer más que eso:

class MyWindow : Window 
{ 
    Button _button; 
    int _numClicked = 0; 

    public MyWindow(string localStringParam) 
    { 
     string localStringVar = "a local variable"; 
     this._button = new Button(); 
     // place the button in the window 
     this._button.Click += new RoutedEventHandler(
      delegate(object sender, RoutedEventArgs args) 
      { 
       this._numClicked += 1; 
       MessageBox.Show("Param was: " + localStringParam + 
        " and local var " + localStringVar + 
        " button clicked " + this._numClicked + " times"); 
      }); 
    } 
} 

Aquí hemos creado un delegado anónimo - una función que no se le da un nombre explícito. La única forma de referirse a esta función es usar el objeto delegado RoutedEventHandler. Además, esta función existe en el ámbito del constructor MyWindow, por lo que puede acceder a todos los parámetros locales, variables y la instancia miembro "this". Continuará manteniendo referencias a las variables y parámetros locales incluso después de que el constructor MyWindow salga.

Como nota al margen, el delegado también mantendrá una referencia a la instancia del objeto - "esto" - incluso después de que se eliminen todas las demás referencias a la clase a. Por lo tanto, para garantizar que una clase es basura, todos los delegados a un método miembro no estático (o delegados creados en el alcance de uno) deben ser eliminados.

+0

Todas las respuestas fueron útiles y complementarias. Gracias a todos ustedes. Leeré con cuidado todas las respuestas y seguiré intentándolo. Gracias de nuevo: D –

5

Bueno, un delegado es un tipo. Las variables de un tipo de delegado pueden hacer referencia o señalar a una función.

Esto le proporciona una forma indirecta de llamar a un método, por lo que los métodos se pueden seleccionar en tiempo de ejecución. Y así puede tener variables, parámetros y propiedades que contengan un método. Las propiedades se llaman eventos.

Sólo uno más ejemplo de código, para ser completa:

delegate void ADelegate(); // the delegate type 

    void Foo() { ... } // a compatible method 
    void Bar() { ... } // a compatible method 

    void Main() 
    { 
     ADelegate funcPtr; // a delegate variable 

     if (aCondition) 
     funcPtr = Foo; // note: _not_ Foo(), Foo is not executed here 
     else 
     funcPtr = Bar; 

     funcPtr(); // calls Foo or Bar depending on aCondition 
    } 

Uso de variables de delegado no es común. Pero puede usar un parámetro de delegado para, por ejemplo, un método de clasificación para seleccionar una ordenación ascendente o descendente.

delegate int Compare(MyClass a, MyClass b); // the delegate type 

    void int CompareUp(MyClass a, MyClass b) { ... } 
    void int CompareDn(MyClass a, MyClass b) { ... } 

    void Sort(MyClass[] data, Compare comparer) { ... } 

Y probablemente conozca eventos, que son (un tipo especial de) propiedades basadas en delegados.

2

Es un principio de inversión. Normalmente, escribe un código que llama a un método y el método que llama se conoce en el momento en que escribe el código. Los delegados le permiten invocar métodos anónimamente. Es decir, usted no sabe el método real que se llama cuando el programa se está ejecutando.

Es útil para separar las preocupaciones de las diferentes partes de una aplicación. Entonces puede tener algún código que realice tareas en un almacén de datos. Es posible que tenga otro código que procese los datos. Los procesos en los datos no necesitan conocer la estructura del almacén de datos y el almacén de datos no debe depender de los usos de los datos.

El código de procesamiento se puede escribir suponiendo ciertas cosas sobre los datos que son independientes de la estructura del almacén de datos. De esta forma, podemos cambiar la estructura del almacén de datos con menos preocupación por afectar los procesos en los datos.

2

Puede pensar en los delegados como una forma de ver el código como datos. Si crea un delegado, es un tipo. Las variables de este tipo pueden apuntar a métodos específicos (que cumplen con la definición de delegado).

Eso significa que puede tratar un fragmento de código como datos y, por ejemplo, pasarlo a un método. Como los delegados apuntan al código (o nulo), también puede invocar el código al que apunta a través de la variable.

Eso permite algunos patrones muy útiles. El ejemplo clásico es cómo ordenar una colección. Al permitir que la persona que llama proporcione un delegado que implemente lo que significa ordenar elementos específicos, el método de clasificación no tiene que saber nada al respecto.

La misma idea se usa ampliamente con muchos métodos para LINQ. Es decir. Usted transfiere un delegado (o más comúnmente un lambda) que maneja una tarea específica, y el método LINQ en cuestión lo llamará para completar la tarea.

2

Un delegado es un tipo de referencias que invoca uno o más métodos a través de la instancia de delegado. Tiene una referencia de los métodos. Los delegados se pueden usar para manejar (invocar/invocar) múltiples métodos en un solo evento. Los delegados se pueden usar para definir métodos asincrónicos. Aquí hay un ejemplo para el delegado Primero creamos una clase. En la cual declaramos el delegado. Y creamos un método dentro de la clase en la que invocamos al delegado.

public class simpleinterest 
{ 
    public delegate void intcal(double i); //declare delegate 
    public event intcal interest; //create delegate object 
    public void calculate(double p, double n,double r) 
    { 
     interest(p*n*r/100); //invoke delegate 
    } 

} 

Dentro de nuestro programa hacemos la asignación. Es decir, especificamos qué evento se activará cuando se invoque al delegado.

private void btn_Click(object sender, RoutedEventArgs e) 
    { 
     simpleinterest s1 = new simpleinterest(); 
     s1.interest+=new simpleinterest.intcal(s1_interest);//mapping 

     s1.calculate(1000,3,10); 

    } 
    void s1_interest(double r) 
    { 
     MessageBox.Show("Amount:" + r.ToString()); 

    } 
4

Los delegados son punteros de función a un método definido en alguna parte.

Suponga que tiene un clase CuentaBancaria y hay que enviar un correo electrónico a un cliente cada vez que él/ella balance es menos de $ 100. Entonces, la tendencia natural es agregar un cheque en el ajustador de la propiedad Balance para ver si el saldo del cliente es inferior a $ 100, y si es así, desencadenar un correo electrónico. Pero este diseño no es flexible.

contras del enfoque anterior:

En el futuro, definitivamente habrá un requisito para enviar un mensaje de texto en lugar de un correo electrónico al cliente. Pocos clientes optan por correo electrónico y mensaje de texto. Por lo tanto, cuando necesite notificar al cliente de cualquier cambio, irá y modificará la clase BankAccount. Esto es una violación de ABIERTO PARA LA EXTENSIÓN Y CERRADO PARA LA MODIFICACIÓN sólidos principios de diseño.

solución utilizando DELEGADOS alternativos:

  1. definir un NotifyCustomer() método, que se ocupa de enviar una notificación al cliente sobre el saldo bajo, fuera de la claseCuentaBancaria.

  2. Modificar la clase BankAccount para definir un delegado y aceptar el valor para él en los constructores.

  3. bien la creación de la clase CuentaBancaria, pasar en el método NotifyCustomer() creado en el paso 1.

  4. En colocador Equilibrio clase del CuentaBancaria, comprobar si el balance es menos de $ 100. Si es así, invoque al delegado.

  5. El NotifyCustomer() método definido fuera de la clase CuentaBancaria se invoca, lo que resulta en el envío de una notificación tal como se define.

En el futuro, si hay una nueva forma de notificar al cliente, entonces no se requieren cambios en el clase CuentaBancaria.

Pros del diseño utilizando los delegados:

  1. acoplamiento débil: La claseCuentaBancaria no sabe nada de la lógica no modificable notificar a un cliente.

  2. Sigue ABIERTO DE AMPLIACIÓN Y CERRADO DE MODIFICACIÓN principio: Cada vez que el medio de comunicación de los cambios de los clientes, que no están obligados a cambiar la clase CuentaBancaria. Así que ahora estará orgulloso de decir que su diseño de clase BankAccount sigue un principio de diseño.

Si se quiere entender más acerca de delegado con un ejemplo, lea What are delegates and why we need them.

1

Los delegados son del tipo de referencia, un delegado se refiere a un método. Esto se llama encapsular el método. Cuando crea un delegado, especifica una firma de método y un tipo de devolución. Puede encapsular cualquier método de coincidencia con ese delegado. Se crea un delegado con delegado palabra clave, seguido de un tipo de retorno y las firmas de los métodos que pueden delegarse a la misma, como en el siguiente:

public delegate void HelloFunctionDelegate(string message); 
class Program 
{ 
static void Main() 
{ 
HelloFunctionDelegate del = new HelloFunctionDelegate(Hello); 
del("Hello from Delegate"); 
} 
public static void Hello(string strMessage) 
{ 
Console.WriteLine(strMessage); 
} 
} 

salida es Hola de Delegado

0

Un delegado es una variable de tipo de referencia que apunta la referencia a un método. Todos los delegados se derivan de la clase System.Delegate. Por ejemplo, en Windows Forms o WPF, un evento de método funciona con el concepto de delegados. Este es un ejemplo del uso delagates en C# Introduction to delegates in C#

1

Delagates en C#: define la firma del método que puede invoke.In otras palabras, podemos decir que envuelve la referencia del método si pueden llamada. A continuación se presentan los usos dedelegados:

  1. Proporciona el mecanismo para implementar la funcionalidad de devolución de llamada en el marco .NET.
  2. Proporciona la capacidad de llamar múltiples métodos secuencialmente.
  3. Tiene la capacidad de implementar llamadas a métodos asíncronos.

Admite métodos estáticos y de instancia.

A continuación se muestra la explicación de cómo funciona internamente.

// Aquí está la declaración de los delegados.

public delegate void DisplayNamme(string name); 

en tiempo de ejecución CLR crea una clase para los delegados como se muestra a continuación.

public class DisplayNamme : System.MulticastDelegate{ 

    // It is a contructor 
    public DisplayNamme(Object @object, IntPtr method); 

    // It is the method with the same prototype as defined in the source code. 
    public void Invoke(String name); 

// This method allowing the callback to be asynchronouslly. 

public virtual IAsyncResult BeginInvoke(String name, 
AsyncCallback callback, Object @object); 

public virtual void EndInvoke(IAsyncResult result); 

} 

Podemos ver a través de Ildasm.exeherramienta. Utilice esta herramienta para descomponer la DLL.

El constructor tiene dos parámetros: IntPrt hace referencia al nombre del método que se pasa a la función, y @object hace referencia implícitamente a la referencia del objeto que se pasa al constructor.

CLR usa el método Invoke de delegados para llamar al método de devolución de llamada.

A continuación se muestra la implementación del método de devolución de llamada utilizando delegados.

// Declare Delegates 
    public delegate void DisplayNamme(string name); 
    class Program 
    { 
     public static void getName(string name) 
     { 
      Console.WriteLine(name); 
     } 
     public static void ShowName(DisplayNamme dn, string name) 
     { 
     // callback method calling. We can call it in two ways. 
      dn(name); 
      // or explicitly 
      dn.Invoke(name); 
    } 
     static void Main(string[] args) 
     { 
      DisplayNamme delDN = getName; 
      Program.ShowName(delDN, "CallBack"); 
      Console.ReadLine(); 
     } 
    } 
1

1) Primero hay que entender por qué/cuando se necesita un delegado, ¿cuál es el problema que resolver.

En mi experiencia los uso principalmente a permiten a un usuario personalizar el comportamiento de un objeto.

Immagine a Rejilla componente que permite al desarrollador personalizar cómo se representará cada columna. Por ejemplo, desea escribir un valor de color rojo cuando es un número bajo cero.

El desarrollador que crea la Grilla no sabe cómo el usuario desea personalizar la salida por lo que necesita un mecanismo que permita al usuario del componente inyectar algo de lógica en el componente.

2) A continuación, usted tiene que entender cómo funciona el delegado

Cuál es confuso es el código extraña usted tiene que escribir para hacerlo, y los muchas maneras que tiene que hacer lo mismo cosa.

Esta es la clase de cuadrícula:

// the grid let the programmer that will use it to customize the output 
public class Grid{ 

    // 1) First I declare only the interface of the delegate 
    public delegate String ValueFormatterDelegate(String v); 

    // 2) I declare a handler of the implementation of the delegate 
    public ValueFormatterDelegate ValueFormatterHandler; 

    // 3) I call the handler inside the Print method 
    public void Print(String x){ 
     Console.WriteLine(ValueFormatterHandler.Invoke(x)); 
    } 

} 

// 1) En primer lugar me declaran sólo la interfaz del delegado pública delegado cadena ValueFormatterDelegate (String v);

Tenga en cuenta que es como un método normal, pero:

  • tiene una palabra clavedelegado
  • de TI no tiene una aplicación

De esta manera me diga: "el método que formateará la salida tiene esta interfaz: tomará una cadena como entrada y dará como salida una cadena"

Me recuerda una definición de un método de una interfaz.

// 2) Declaro un controlador de la aplicación de la delegada pública ValueFormatterDelegate ValueFormatterHandler;

Ahora tengo que crear una propiedad del tipo del delegado que manejará la implementación de este método.

// 3) Tiene la palabra el controlador dentro del método de impresión public void impresión (String x) {Console.WriteLine (ValueFormatterHandler.Invocar (x)); }

Dentro del método de impresión puedo usar el controlador que enlazará la implementación real.

ValueFormatterHandler es de tipo ValueFormatterDelegate y ValueFormatterDelegate es ad delegado y .Invoke es un método de tipo delegado

Este es un programa que utilice mi clase de cuadrícula y es capaz de personalizar sobre la marcha. El problema aquí es las muchas formas en que tiene que hacer lo mismo.

using System; 

public class Program{ 
    public static void Main(){ 

     var printer = new Printer(); 

     // METHOD 1 : link to a named method 
     // here i link the handler of the delegate to a real method 
     // the FormatXXX is a static method defined at the ed of this code 
     printer.ValueFormatter = FormatXXX; 

     // when i call Print("hello") 
     printer.Print("hello"); // XXhelloXX 

     // METHOD 2 : anonimous method 
     // think at this like a method but without a name 
     // FormatYY (String x){ return "YY"+x+"YY"; }; 
     // become 
     // delegate (String x){ return "YY"+x+"YY"; }; 
     printer.ValueFormatter = delegate (String x){ return "YY"+x+"YY"; }; 
     printer.Print("hello"); // YYhelloYY 

     // METHOD 3 : anonimous method using lambda 
     // as you can note the type of parameter x is inferred from the delegate declaration 
     // public delegate String ValueFormatterDelegate(String v); 
     printer.ValueFormatter = (x)=>"KK" + x + "KK"; 

    } 

    public static String FormatXXX(String y){ 
     return "XX"+ y +"XX"; 
    } 

}