Mostrando las entradas con la etiqueta Eventos. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Eventos. Mostrar todas las entradas

martes, 21 de julio de 2009

Diferencias entre Eventos y Delegados - En qué son distintos?

Eventos versus Delegados (Events vs Delegates)

Es la típica pregunta acerca de la diferencia entre eventos y delegados, considerando que uno siempre podría imitar la funcionalidad de un evento utilizando un delegado. Esto considerando que un evento siempre está relacionado a un delegado.

Un evento es parecido a un multicast delegate en algún sentido.

Veamos el siguiente ejemplo donde tenemos msgNotifier (evento) y msgNotifier2 (delegado simple) que aparentan comportarse exactamente igual en todo sentido.

using System;

namespace EventAndDelegate
{
delegate void MsgHandler(string s);

class Class1
{
public static event MsgHandler msgNotifier;
public static MsgHandler msgNotifier2;
[STAThread]
static void Main(string[] args)
{
Class1.msgNotifier += new MsgHandler(PipeNull);
Class1.msgNotifier2 += new MsgHandler(PipeNull);
Class1.msgNotifier("test");
Class1.msgNotifier2("test2");
}

static void PipeNull(string s)
{
return;
}
}
}


Si vemos el código IL para el método Main vemos que ambos msgNotifier y msgNotifier2 son usados de la misma manera.

.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 )
// Code size 95 (0x5f)
.maxstack 4
IL_0000: ldsfld class EventAndDelegate.MsgHandler EventAndDelegate.Class1::msgNotifier
IL_0005: ldnull
IL_0006: ldftn void EventAndDelegate.Class1::PipeNull(string)
IL_000c: newobj instance void EventAndDelegate.MsgHandler::.ctor(object,
native int)
IL_0011: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
class [mscorlib]System.Delegate)
IL_0016: castclass EventAndDelegate.MsgHandler
IL_001b: stsfld class EventAndDelegate.MsgHandler EventAndDelegate.Class1::msgNotifier
IL_0020: ldsfld class EventAndDelegate.MsgHandler EventAndDelegate.Class1::msgNotifier2
IL_0025: ldnull
IL_0026: ldftn void EventAndDelegate.Class1::PipeNull(string)
IL_002c: newobj instance void EventAndDelegate.MsgHandler::.ctor(object,
native int)
IL_0031: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
class [mscorlib]System.Delegate)
IL_0036: castclass EventAndDelegate.MsgHandler
IL_003b: stsfld class EventAndDelegate.MsgHandler EventAndDelegate.Class1::msgNotifier2
IL_0040: ldsfld class EventAndDelegate.MsgHandler EventAndDelegate.Class1::msgNotifier
IL_0045: ldstr "test"
IL_004a: callvirt instance void EventAndDelegate.MsgHandler::Invoke(string)
IL_004f: ldsfld class EventAndDelegate.MsgHandler EventAndDelegate.Class1::msgNotifier2
IL_0054: ldstr "test2"
IL_0059: callvirt instance void EventAndDelegate.MsgHandler::Invoke(string)
IL_005e: ret
} // end of method Class1::Main


Si vemos en el MSDN keywords list vemos que los event son solo modificadores (modifier).

La pregunta es que cosa exactamente modifican?

Valor agregado de un evento
Eventos e interfaces

Un evento puede ser incluido en la declaración de una interface mientras que un campo no.

interface ITest
{
event MsgHandler msgNotifier; // compila
MsgHandler msgNotifier2; // error CS0525: Interfaces cannot contain fields
}

class TestClass : ITest
{
public event MsgHandler msgNotifier; // cuando se implementa una interface se debe implementar el evento también.
static void Main(string[] args) {}
}


Invocation de un evento

Un evento solo puede ser invocado en la clase en la que se declaró, mientras que un delegado puede ser invocado desde cualquier parte.

Ejemplo:

using System;

namespace EventAndDelegate
{
delegate void MsgHandler(string s);

class Class1
{
public static event MsgHandler msgNotifier;
public static MsgHandler msgNotifier2;

static void Main(string[] args)
{
new Class2().test();
}
}

class Class2
{
public void test()
{
Class1.msgNotifier("test"); // error CS0070: The event 'EventAndDelegate.Class1.msgNotifier' can only appear on the left hand side of += or -= (except when used from within the type 'EventAndDelegate.Class1')
Class1.msgNotifier2("test2"); // compiles fine
}
}
}

Esta restricción es muy fuerte y aún las clases derivadas no pueden invocar dicho evento (salvo un método protected virtual que lo dispare).

Accesores de Eventos
Eventos disponen de métodos add y remove.
Esto es similar al get y set de las properties.

Firma del evento

el .NET framework agrega una restriction en la firma de los delegados que pueden ser usados como eventos. La firma tienen que ser en este estilo foo(object source, EventArgs e), donde source es el objeto que dispara el evento y e contiene información específica a este evento.

Estas serían a mi entender algunas diferencias entre eventos (events) y delegates (delegados)

domingo, 21 de junio de 2009

Como exponer los eventos RowUpdated y RowUpdating en un TableAdapter?

Bueno, después de dar un par de vueltas e investigar un poco, acá les dejo la solución a como exponer los eventos RowUpdated y RowUpdating de un TableAdapter de los generados con diseñador de Dataset fuertemente Tipados.

Tanto en C# 2005 como en C# 2008 tienen que ir al Dataset tipado (xsd).
Identifican al TableAdapter (NO el datatable) que van a modificar y clickean con boton izquierdo.
Van a la ventana de propiedades para el tableadapter y ponen la propidad ConnectionModifier en PUBLIC.

Después clickean con el botón derecho en el mismo TableAdapter y van a la opción Ver Código (View Code) y en la ventana de código agregan la clase parcial necesaria para incorporar el código que expone el dataadapter interno.

El código a continuación es un ejemplo de como debiera queda la clase

namespace WinCon.DataSet1TableAdapters //obviamente acá va el namespace de ustedes
{
using System.Data;
using System.Data.SqlClient; //acá va el provider de la base que ustedes están usando
// en caso que no sea SQL Server

public partial class EjemploTableAdapter //de nuevo, el nombre exacto de su tableadapter
{
public System.Data.SqlClient.SqlDataAdapter Adapter2 //el nombre que se les ocurra
{
get
{
this.Adapter.SelectCommand = this.CommandCollection[0];
return this.Adapter;
}
}
}
}

Hasta acá la modificación, si lo estuvieran invocando por ejemplo desde un windows forms, podrían ejecutar lo siguiente (ojo que ahora el código que viene a continuación está adentro de un windows forms que utiliza al tableadapter).

DataSet1TableAdapters.EjemploTableAdapter da = new WinCon.DataSet1TableAdapters.EjemploTableAdapter();
DataSet1 ds = new DataSet1();


public Form1()
{
//atacho los eventos y digo que lo gestionan
// los métodos metodo y metodo2 respectivamente.
da.Adapter2.RowUpdating += metodo; //fijensé que uso la propiedad Adapter2
// que creé adentro del TableAdapter.
da.Adapter2.RowUpdated += metodo2;
InitializeComponent();
}


Espero que a alguien le sea de utilidad.

Hugo Bernachea
http://www.linkedin.com/in/bernachea