usar - Delegados en C#
porque usar delegados (10)
Bueno, un delegado es un tipo. Las variables de un tipo de delegado pueden hacer referencia o apuntar a una función.
Esto le da 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 contienen un método. Las propiedades se llaman eventos.
Solo un ejemplo de código más, para completar:
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
}
El uso de variables delegadas no es común. Pero puede usar un parámetro delegado para, por ejemplo, un método de clasificación para seleccionar una clasificació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 los eventos, que son (un tipo especial de) propiedades basadas en delegados.
Estoy teniendo algunos problemas para entender cómo trabajan los delegados en C #. Tengo muchos ejemplos de código, pero todavía no pude entenderlo correctamente.
¿Puede alguien explicármelo en "inglés simple"? ¡Por supuesto! Los ejemplos de código ayudarán, pero creo que necesito más una descripción de cómo y por qué funciona.
EDITAR:
Bueno, la pregunta es: ¿por qué trabajan los delegados? ¿Qué es un "diagrama de flujo" de todo el proceso?
¿Cuáles son los requisitos previos para utilizar delegados?
Espero que esto aclare la pregunta.
Es un principio de inversión. Normalmente, usted escribe un código que llama a un método y el método al que llama se conoce en el momento en que escribe el código. Los delegados le permiten invocar métodos de forma anónima. Es decir, no conoce el método real al que se llama cuando el programa se está ejecutando.
Es útil para separar las preocupaciones de diferentes partes de una aplicación. Por lo tanto, puede tener algún código que realice tareas en un almacén de datos. Es posible que tenga otro código que procesa 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 puede escribirse asumiendo ciertas cosas sobre los datos que son independientes de la estructura del almacén de datos. De esa manera, podemos cambiar la estructura del almacén de datos sin preocuparnos por afectar los procesos en los datos.
Los delegados son tipo de referencia, un delegado se refiere a un método. Esto se llama encapsular el método. Cuando creas un delegado, especificas la firma de un método y el tipo de retorno. Puede encapsular cualquier método coincidente con ese delegado. Usted crea un delegado con la palabra clave delegada , seguido por un tipo de retorno y las firmas de los métodos que se le pueden delegar, como se muestra a continuación:
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);
}
}
La salida es Hola de Delegado
Puede pensar en los delegados como una forma de ver el código como datos. Si creas 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 implementa lo que significa ordenar elementos específicos, el método de clasificación no tiene que saber nada sobre esto.
La misma idea se usa ampliamente con muchos métodos para LINQ. Es decir, se pasa un delegado (o más comúnmente un lambda) que maneja alguna tarea específica, y el método LINQ en cuestión lo llamará para completar la tarea.
Un delegado es un tipo de referencia que invoca métodos únicos / múltiples a través de la instancia del delegado. Contiene una referencia de los métodos. Las delegaciones se pueden usar para manejar (llamar / invocar) múltiples métodos en un solo evento. Los delegados pueden ser utilizados para definir métodos asíncronos. Aquí hay un ejemplo para 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 el mapeo. 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());
}
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 de delagates en C # Introducción a los delegados en C #
Una forma de pensar acerca de un delegado es como una referencia a una función . Por ejemplo, supongamos que tiene un botón en una ventana y desea que suceda algo cuando se hace clic en el botón. Puede adjuntar un delegado al evento Click del botón y, cuando 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 de ButtonClicked una función estática. A continuación, quiero hacer un punto sobre las funciones no estáticas. Supongamos que, en cambio, 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 la instancia "this", en la que se llama al método. La instancia "esto" en el constructor MyWindow y "esto" en ButtonClicked son las mismas.
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 "esto" 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í creamos un delegado anónimo , una función que no recibe 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 alcance del constructor MyWindow, por lo que puede acceder a todos los parámetros locales, variables y la instancia miembro "esto". 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 todas las demás referencias a la clase a eliminadas. Por lo tanto, para garantizar que una clase se recolecta como basura, todos los delegados a un método de miembro no estático (o delegados creados en el ámbito de uno) deben eliminarse.
1) Primero debe entender por qué / cuando necesita un delegado, cuál es el problema que resuelve.
En mi experiencia, los uso principalmente para permitir que un usuario personalice el comportamiento de un objeto .
Imagine un componente de Grid que permita al desarrollador personalizar cómo se representará cada columna. Por ejemplo, quiere escribir un valor de color rojo cuando es un número bajo cero.
El desarrollador que crea el Grid 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) Entonces tienes que entender cómo funciona el delegado
Lo que es confuso es el código extraño que tienes que escribir para hacerlo, y las muchas formas en que debes hacer lo mismo.
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) Primero declaro solo la interfaz del delegado public Strate ValueFormatterDelegate (String v);
Note que es como un método normal pero:
- tiene una palabra clave delegada
- no tiene implementacion
De esta manera digo: "el método que formateará la salida tiene esta interfaz: tomará una cadena como entrada y generará una cadena"
Me recuerda una definición de un método de una interfaz.
// 2) Declaro un manejador de la implementación del delegado público ValueFormatterDelegate ValueFormatterHandler;
Ahora tengo que crear una propiedad del tipo del delegado que manejará la implementación de este método.
// 3) Llamo al controlador dentro del método Print public void Print (String x) {Console.WriteLine (ValueFormatterHandler.Invoke (x)); }
Dentro del método de impresión, puedo usar el controlador que vinculará la implementación real.
ValueFormatterHandler es de tipo ValueFormatterDelegate y ValueFormatterDelegate es un delegado de anuncios y .Invoke es un método de tipo delegado
Este es un programa que usa mi clase Grid y puede personalizarlo sobre la marcha. El problema aquí es las muchas formas en que tienes 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";
}
}
Delagates en c #: define la firma del método que puede invocar. En otras palabras, podemos decir que envuelve la referencia del método al que puede llamar. A continuación se presentan los usos de los delegados :
- Proporciona el mecanismo para implementar la funcionalidad de devolución de llamada en el marco .NET.
- Proporciona la capacidad de llamar múltiples métodos secuencialmente.
- Tiene la capacidad de implementar llamadas de método asíncrono.
Es compatible tanto con métodos estáticos como 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 el 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 verlo a través de la herramienta ILDasm.exe . Utilice esta herramienta para romper el DLL.
El constructor tiene dos parámetros: IntPrt refiere el nombre del método que se pasa a la función, y @object refiere la referencia del objeto que se pasa al constructor de manera implícita.
CLR utiliza el método Invocar de los 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();
}
}
Los delegados son punteros de función a un método definido en algún lugar.
Supongamos que tiene una clase de BankAccount y que tiene que enviar un correo electrónico a un cliente siempre que su saldo sea inferior a $ 100. Entonces, la tendencia natural es agregar un cheque en el establecedor de propiedades de Balance para ver si el saldo del cliente es menor a $ 100, y si es así, activar 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. Entonces, cuando necesite notificar al cliente de cualquier cambio, irá y modificará la clase de BankAccount . Esto es una violación de los principios de diseño sólido de ABIERTO POR EXTENSIÓN Y CERRADO POR MODIFICACIÓN .
Solución alternativa utilizando DELEGADOS:
Defina un método NotifyCustomer () , que trata de enviar una notificación al cliente sobre el saldo bajo, fuera de la clase BankAccount .
Modifique la clase BankAccount para definir un delegado y acepte su valor en los constructores.
Al crear la clase BankAccount , pase el método NotifyCustomer () creado en el paso 1.
En el establecedor de saldo de la clase de BankAccount, verifique si el saldo es inferior a $ 100. Si es así, invoca al delegado.
El método NotifyCustomer () definido fuera de la clase BankAccount se invoca, lo que resulta en el envío de una notificación como se define.
En el futuro, si hay una nueva forma de notificar al cliente, entonces no se requieren cambios en la clase de Cuenta de Banco .
Ventajas del diseño mediante delegados:
ACOPLAMIENTO LOOSE : La clase BankAccount no conoce la lógica codificada que notifica a un cliente.
Sigue ABIERTO PARA EXTENSIÓN Y CERRADO PARA EL MODO DE MODIFICACIÓN : Siempre que el medio para notificar cambios al cliente, no está obligado a cambiar la clase de Cuenta de Banco . Así que ahora estará orgulloso de decir que el diseño de su clase de BankAccount sigue un principio de diseño.
Si desea saber más sobre delegado con un ejemplo, lea Qué son los delegados y por qué los necesitamos.