La traducción del artículo fue preparada específicamente para estudiantes del curso "C # Developer" .

¿Qué son los eventos en C #?
Un evento se puede utilizar para proporcionar notificaciones. Puede suscribirse al evento si necesita estas notificaciones. También puede crear sus propios eventos que le notificarán que algo que le interesa ha sucedido. .NET Framework ofrece tipos integrados que puede usar para crear eventos. Usando delegados, expresiones lambda y métodos anónimos, puede crear y usar eventos de una manera conveniente.
Comprender a los delegados en C #
En C #, los delegados forman los bloques de construcción básicos para eventos. Un delegado es un tipo que define la firma de un método. Por ejemplo, en C ++, esto se puede hacer usando un puntero de función. En C #, puede crear una instancia de un delegado que apunte a otro método. Puede llamar a este método a través de la instancia de delegado.
El siguiente es un ejemplo de declarar un delegado y llamar a un método a través de él.
Usar un delegado en C #
}class Program { public delegate double MathDelegate(double value1, double value2); public static double Add(double value1, double value2) { return value1 + value2; } public static double Subtract(double value1, double value2) { return value1 - value2; } Console.ReadLine(); public static void Main() { MathDelegate mathDelegate = Add; var result = mathDelegate(5, 2); Console.WriteLine(result);
Como puede ver, usamos la palabra clave delegado para decirle al compilador que estamos creando un tipo de delegado.
Configurar delegados es fácil con la creación automática de un nuevo tipo de delegado.
También puede usar la
nueva palabra clave para crear un delegado.
MathDelegate mathDelegate = new MathDelegate(Add)
;
Un delegado instanciado es un objeto; También puede usarlo y pasarlo como argumento a otros métodos.
Delegados de multidifusión en C #
Otra gran característica de los delegados es que pueden combinarlos. Esto se llama multidifusión. Puede usar el operador + o + = para agregar otro método a la lista de llamadas de una instancia de delegado existente. Del mismo modo, también puede eliminar un método de la lista de llamadas utilizando el operador de asignación de decremento (- o - =). Esta característica sirve como base para eventos en C #. El siguiente es un ejemplo de un delegado de multidifusión.
class Program { static void Hello(string s) { Console.WriteLine(" Hello, {0}!", s); } static void Goodbye(string s) { Console.WriteLine(" Goodbye, {0}!", s); } delegate void Del(string s); static void Main() { Del a, b, c, d;
Esto es posible porque los delegados heredan de la clase
System.MulticastDelegate
, que a su vez hereda de
System.Delegate
. Puede usar los miembros definidos en estas clases base para sus delegados.
Por ejemplo, para averiguar cuántos métodos llamará un delegado de multidifusión, puede usar el siguiente código:
int invocationCount = d.GetInvocationList().GetLength(0);
Covarianza y contravarianza en C #
Cuando asigna un método a un delegado, la firma del método no tiene que coincidir exactamente con el delegado. Esto se llama covarianza y contravarianza. La covarianza permite que un método tenga un tipo de retorno más derivado que el definido en el delegado. La contravarianza permite un método con tipos de parámetros que son menos derivados que los tipos en el delegado.
Delegar covarianza
Aquí hay un ejemplo de covarianza,
class Program { public delegate TextWriter CovarianceDel(); public static StreamWriter MethodStream() { return null; } public static StringWriter MethodString() { return null; } static void Main() { CovarianceDel del; del = MethodStream; del = MethodString; Console.ReadLine(); } }
Dado que
StreamWriter
y
StringWriter
heredan de
TextWriter
, puede usar
CovarianceDel
con ambos métodos.
Contravarianza en delegados
El siguiente es un ejemplo de contravarianza.
class Program { public static void DoSomething(TextWriter textWriter) { } public delegate void ContravarianceDel(StreamWriter streamWriter); static void Main() { ContravarianceDel del = DoSomething; Console.ReadLine(); } }
Dado que el método
DoSomething
puede funcionar con
TextWriter
, ciertamente puede funcionar con
StreamWriter
. Debido a la contravarianza, puede llamar a un delegado y pasar una instancia de
StreamWriter
al método
DoSomething
.
Puede obtener más información sobre este concepto
aquí .
Expresiones lambda en C #
A veces, una firma de método completa puede requerir más código que el cuerpo del método en sí. También hay situaciones en las que necesita crear un método completo solo para usarlo como delegado.
Para estos casos, Microsoft ha agregado algunas características nuevas en C #, como los métodos anónimos en 2.0. En C # 3.0, las cosas mejoraron aún más cuando se agregaron expresiones lambda. La expresión lambda es la forma preferida al escribir código nuevo.
El siguiente es un ejemplo de la última sintaxis lambda.
class Program { public delegate double MathDelegate(double value1, double value2); public static void Main() { MathDelegate mathDelegate = (x,y) => x + y; var result = mathDelegate(5, 2); Console.WriteLine(result); // : 7 mathDelegate = (x, y) => x - y; ; result = mathDelegate(5, 2); Console.WriteLine(result); // : 3 Console.ReadLine(); } }
Para leer este código, debe usar la palabra "sigue" en el contexto de la sintaxis lambda especial. Por ejemplo, la primera expresión lambda en el ejemplo anterior dice "x e y siguen a la suma de x e y".
Una función lambda no tiene un nombre específico, a diferencia de un método. Debido a esto, las lambdas se denominan funciones anónimas. Tampoco es necesario que especifique explícitamente el tipo del valor de retorno. El compilador lo asume automáticamente desde su lambda. Y en el caso del ejemplo anterior, los tipos de parámetros x e y tampoco se especifican explícitamente.
Puede crear lambdas que abarquen múltiples operadores. Puede hacer esto agregando llaves alrededor de las declaraciones que componen la lambda, como se muestra en el siguiente ejemplo.
MathDelegate mathDelegate = (x,y) => { Console.WriteLine("Add"); return x + y; };
A veces, el anuncio de un delegado para un evento parece un poco engorroso. Debido a esto, .NET Framework tiene varios tipos de delegados integrados que puede usar al declarar delegados. En el ejemplo MathDelegate, usó el siguiente delegado:
public delegate double MathDelegate(double value1, double value2);
Puede reemplazar este delegado con uno de los tipos integrados, a saber,
Func <int, int, int>
.
asi
class Program { public static void Main() { Func<int, int, int> mathDelegate = (x,y) => { Console.WriteLine("Add"); return x + y; }; var result = mathDelegate(5, 2); Console.WriteLine(result);
Los tipos de funciones <...> se pueden encontrar en el espacio de nombres del sistema. Representan delegados que devuelven un tipo y toman de 0 a 16 parámetros. Todos estos tipos se heredan de System.MulticaseDelegate para que pueda agregar varios métodos a la lista de llamadas.
Si necesita un tipo de delegado que no devuelve un valor, puede usar los tipos System.Action. También pueden tomar de 0 a 16 parámetros, pero no devuelven un valor.
Aquí hay un ejemplo del uso del tipo Acción,
class Program { public static void Main() { Action<int, int> mathDelegate = (x,y) => { Console.WriteLine(x + y); }; mathDelegate(5, 2);
Puede obtener más información sobre los delegados integrados de .NET
aquí .
Las cosas se complican cuando su función lambda comienza a referirse a variables declaradas fuera de la expresión lambda o a esto. Normalmente, cuando un control abandona el alcance de una variable, la variable deja de ser válida. Pero, ¿y si el delegado se refiere a una variable local? Para solucionar esto, el compilador genera código que extiende la vida útil de la variable capturada, al menos tanto como el delegado de mayor duración. Esto se llama cierre.
Puede obtener más información sobre cierres
aquí .
Eventos en C #
Considere un patrón de desarrollo popular: editor-suscriptor (pub / sub). Puede suscribirse a un evento y luego se le notificará cuando el editor del evento inicie un nuevo evento. Este sistema se utiliza para establecer una comunicación débil entre componentes en una aplicación.
El delegado forma la base del sistema de eventos en C #.
Un evento es un tipo especial de delegado que facilita la programación orientada a eventos. Los eventos son miembros de una clase a la que no se puede llamar fuera de la clase, independientemente del especificador de acceso. Entonces, por ejemplo, un evento declarado como público permitiría que otras clases usen + = y - = para este evento, pero activar un evento (es decir, llamar a un delegado) está permitido solo en la clase que contiene el evento. Veamos un ejemplo,
Luego, un método en otra clase puede suscribirse al evento agregando uno de sus métodos al delegado del evento:
El siguiente es un ejemplo que muestra cómo una clase puede proporcionar un delegado abierto y generar un evento.
class Program { static void Main(string[] args) {
Incluso si el evento se declara como
public
, no se puede activar directamente en ninguna parte, excepto en la clase en la que se encuentra.
Usando la palabra clave del
event
, el compilador protege nuestro campo del acceso no deseado.
Y tambien
No permite el uso de = (asignación de delegado directo). Por lo tanto, su código ahora está protegido del riesgo de eliminar suscriptores anteriores mediante el uso de = en lugar de + =.
Además, puede observar la sintaxis especial de inicialización de campo OnChange para un delegado vacío, como
delegate { }
. Esto asegura que nuestro campo OnChange nunca sea nulo. Por lo tanto, podemos eliminar la verificación nula antes de invocar el evento si no hay otros miembros de la clase que lo hagan nulo.
Cuando ejecuta el programa anterior, su código crea una nueva instancia de Pub, se suscribe al evento de dos maneras diferentes y genera un evento llamando a p.Raise. La clase Pub no tiene conocimiento de ninguno de los suscriptores. Simplemente genera un evento.
También puede leer mi artículo,
C # Publisher-Subscriber Design Pattern, para una comprensión más profunda de este concepto.
Bueno, eso es todo por ahora. Espero que entiendas la idea. Gracias por leer el post. Avíseme si hay algún error o si se necesitan cambios en los comentarios a continuación. Gracias de antemano!
Enlaces utiles
www.c-sharpcorner.com/blogs/c-sharp-generic-delegates-func-action-and-predicatedocs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariancehttps://web.archive.org/web/20150707082707/http://diditwith.net/PermaLink,guid,235646ae-3476-4893-899d-105e4d48c25b.aspx