You are currently browsing the category archive for the ‘Delegates’ category.

Hasta la versión 2.0 del Framework.NET, la declaración de un delegado sólo era posible programando métodos con nombre. A partir de la versión 2.0 se introduce el concepto Método Anónimo.

Un método anónimo nos permite definir una función de manera implícita, sin necesidad de declararla dándole un nombre.

Como los Métodos Anónimos no tienen nombre, la forma de invocarlos es a través de un delegado.

Usar un Método Anónimo es pasar como parámetro a un bloque de código definido como tipo Delegate, sin asignarle un nombre.

Por ejemplo, si en el constructor de un formulario insertamos la siguiente línea de código:

 

  this.Click += delegate { MessageBox.Show(@”Funciona.”); };

 

Al hacer un click sobre el formulario se desplegará un MessageBox que muestre el string “Funciona”.

En el ejemplo podemos ver que en la misma línea se define el Delegate y su implementación. Eso es exactamente un Método Anónimo.

Los Métodos Anónimos reducen la sobrecarga de código al crear instancias de delegados sin tener que crear métodos independientes.
En caso que quisiéramos capturar algún parámetro del evento, sólo tenemos que declarar los argumentos a continuación de la palabra delegate:

 

  this.Click += delegate(object sender, EventArgs e)

  {

      System.Windows.Forms.MessageBox.Show(@”Funciona. “ + e.ToString());

  };

 

Para el uso de Métodos Anónimos existen algunas restricciones:

  • No se puede invocar código no manejado en un Método Anónimo.
  • Las variables declaradas en el mismo bloque donde se declara un Método Anónimo se llaman Externas (Outer) al Método Anónimo. No pueden ser accedidos parámetros de ámbito externo calificados con ref u out.
  • En un Método Anónimo no pueden usarse goto, break ni continue si apunta fuera del ámbito del Método Anónimo, tampoco pueden usarse desde fuera del Método Anónimo si el destino se encuentra dentro del bloque del Método Anónimo. Más allá de Método Anónimo, siempre es bueno evitar el uso de goto, break y continue.
  • No pueden usarse Método Anónimo en el lado izquierdo del operador is.

Los Métodos Anónimos son ideales en el manejo de eventos cuando lo que hay que hacer son cosas simples permitiendo un código compacto. Pero no hay que perder de vista que el abuso de esta técnica puede tornar nuestro código como algo muy difícil de leer y entender.

Este post es continuación de este anterior sobre Delegates. Si ya conocés el tema no te va a hacer falta empezar por él.

En .NET tenemos la posibilidad de publicar un evento, al cual uno o más objetos pueden suscribirse.

Cuando se produce el evento, el aplicativo informa a los objetos suscriptos, que en el entorno del evento ejecutarán el código que contengan en la definición de su clase.

Al producirse el evento se invoca un método definido por medio de un delegado

Este Delegate debe recibir dos parámetros de entrada, el primero de tipo Object que recibe el objeto que produce el evento, el segundo es algún objeto que derive de la clase EventArgs.

Vamos a ver un ejemplo con el que jamás podremos a llamar la atención de nadie que nos interese. 🙂

Desde este link pueden descargarlo.

Vamos a tener tres formularios, el primero de ellos tiene un TrackBar que cuando se modifica los otros dos formularios se “enteran” y muestran la modificación a través de un textBox.

El primer punto interesante es que no tengo que agregar nada de código en los formularios que se “enteran” del evento. En el ejemplo que les dejo, en VB.NET y C#, podemos ver que en los Form2 y Form3 sólo hay estructura para verse como formularios y para soportar un textBox.

Empiezo por la clase TrackBarChangeEventsArgs que encapsula el valor que toma el TrackBar cuando se modifica su estado.

[VB]

Imports System

 

”’ <summary>

”’ Encapsula el argumento del evento, en este caso es un entero

”’ que representa al valor del TrackBar.

”’ </summary>

”’ <remarks>La clase hereda de EventArgs.</remarks>

Class TrackBarChangeEventsArgs

    Inherits EventArgs

 

    Private mChange As Integer

 

    ”’ <summary>

    ”’ Constructor

    ”’ </summary>

    ”’ <param name=”pChange”>Valor del TrackBar.</param>

    ”’ <remarks></remarks>

    Public Sub New(ByVal pChange As Integer)

        MyBase.New()

        Me.mChange = pChange

    End Sub

 

    ”’ <summary>

    ”’ Publica el valor del argumento del evento, en este caso

    ”’ el valor del TrackBar.

    ”’ </summary>

    ”’ <value></value>

    ”’ <returns>Valor del TrackBar</returns>

    ”’ <remarks></remarks>

    Public ReadOnly Property Change() As Integer

        Get

            Return mChange

        End Get

    End Property

End Class

 

 

[C#]

using System;

 

/// <summary>

/// Encapsula el argumento del evento, en este caso es un entero

/// que representa al valor del TrackBar.

/// </summary>

/// <remarks>La clase hereda de EventArgs.</remarks>

class TrackBarChangeEventsArgs : EventArgs

{

    private int mChange;

 

    /// <summary>

    /// Constructor

    /// </summary>

    /// <param name=”pChange”>Valor del TrackBar.</param>

    /// <remarks></remarks>

    public TrackBarChangeEventsArgs(int pChange)

    {

        this.mChange = pChange;

    }

 

    /// <summary>

    /// Publica el valor del argumento del evento, en este caso

    /// el valor del TrackBar.

    /// </summary>

    /// <value></value>

    /// <returns>Valor del TrackBar</returns>

    /// <remarks></remarks>

    public int Change

    {

        get

        {

            return mChange;

        }

    }

}

 

Podemos ver que la clase deriva de EventArgs, tiene un constructor que recibe como parámetro el valor del TrackBar y publica al mismo mediante una propiedad Read Only.

Ahora veamos la clase encargada de publicar el evento, en el ejemplo es TrackBarAdministrador, en ella es donde se declara el Delegate de nombre TrackBarChangeEventHandler, que, como mencioné antes, recibe dos parámetros uno de tipo Object que contiene al objeto que disparó el evento y otro que deriva de EventArgs, esta ves es TrackBarChangeEventArgs.

A continuación se declara el evento OnTrackBarChangeHandler del tipo Delegate TrackBarChangeEventHandler.

La clase tiene un único método llamado ChangeTrackBar que acepta un sólo parámetro de tipo entero que es el valor del TrackBar. En este método se declara una variable llamada e del tipo de la clase TrackBarChangeEventsArgs (la que encapsula y publica el valor del TrackBar) y se le pasa el parámetro que se recibió, el entero que representa el valor del TrackBar. Ahora se dispara el evento OnTrackBarChangeHandler pasándole como parámetros el propio objeto (que es una instancia de la clase TrackBarAdministrador) y con esto queda publicado el evento.

Veamos la clase:

[VB]

Imports System

 

”’ <summary>

”’ Administra el evento de cambio de valor del TrackBar.

”’ </summary>

”’ <remarks></remarks>

Class TrackBarAdministrador

    ”’ <summary>

    ”’ Declaro un Delegate de nombre TrackBarChangeEventHandler. A

    ”’ Continuación declaro un evento del tipo del Delegate, que

    ”’ será el evento a propagar.

    ”’ </summary>

    ”’ <param name=”source”>Parámetro de entrada de tipo Object</param>

    ”’ <param name=”e”>Parámetro de entrada de tipo TrackBarChangeEventsArgs</param>

    ”’ <remarks>Esta es la clase que administra el evento a producirse al

    ”’ cambiar el valor del TrackBar</remarks>

    Public Delegate Sub TrackBarChangeEventHandler(ByVal source As Object, ByVal e As TrackBarChangeEventsArgs)

    Public Event OnTrackBarChangeHandler As TrackBarChangeEventHandler

 

    ”’ <summary>

    ”’ Método que recibe el valor a propagar.

    ”’ </summary>

    ”’ <param name=”pChange”>Valor del TrackBar a propagar.</param>

    ”’ <remarks></remarks>

    Public Sub ChangeTrackBar(ByVal pChange As Integer)

        Dim e As TrackBarChangeEventsArgs = New TrackBarChangeEventsArgs(pChange)

        RaiseEvent OnTrackBarChangeHandler(Me, e)

    End Sub

End Class

[C#]

using System;

 

/// <summary>

/// Administra el evento de cambio de valor del TrackBar.

/// </summary>

/// <remarks></remarks>

class TrackBarAdministrador

{

    public event TrackBarChangeEventHandler OnTrackBarChangeHandler;

 

    /// <summary>

    /// Método que recibe el valor a propagar.

    /// </summary>

    /// <param name=”pChange”>Valor del TrackBar a propagar.</param>

    /// <remarks></remarks>

    public void ChangeTrackBar(int pChange)

    {

        TrackBarChangeEventsArgs e = new TrackBarChangeEventsArgs(pChange);

        OnTrackBarChangeHandler(this, e);

    }

 

    /// <summary>

    /// Declaro un Delegate de nombre TrackBarChangeEventHandler. A

    /// Continuación declaro un evento del tipo del Delegate, que

    /// será el evento a propagar.

    /// </summary>

    /// <param name=”source”>Parámetro de entrada de tipo Object</param>

    /// <param name=”e”>Parámetro de entrada de tipo TrackBarChangeEventsArgs</param>

    /// <remarks>Esta es la clase que administra el evento a producirse al

    /// cambiar el valor del TrackBar</remarks>

    public delegate void TrackBarChangeEventHandler(object source, TrackBarChangeEventsArgs e);

}

 

Nos queda por ver la clase engargada de notificar la ocurrencia del evento que en este caso se llama TrackBarObservador.

Si bien la clase tiene este nombre, esto no significa que estemos aplicando el patrón Observer, para quien le interese este patrón acá hay un artículo descriptivo.

Esta clase tiene un constructor que acepta como parámetro un objeto del tipo de la clase TrackBarAdministrador, lo guarda en una variable local y vincula al evento OnTrackBarChangeHandler declarado en la clase TrackBarAdministrador al método OnTrackBarChange definido más adelante en esta misma clase. Este método es donde se realizan las suscripciones de los formularios Form2 y Form3 que serán notificados del cambio en el TrackBar.

[VB]

Imports System

 

”’ <summary>

”’ Informa a los formularios sobre el evento que administra TrackBarAdministrador.

”’ </summary>

”’ <remarks></remarks>

Class TrackBarObservador

    ”’ <summary>

    ”’ Variable local del tipo de la clase que administra el evento

    ”’ a producirse al cambiar el vaor del TrackBar.

    ”’ </summary>

    ”’ <remarks></remarks>

    Private mTrackBarAdministrador As TrackBarAdministrador

 

    ”’ <summary>

    ”’ Constructor

    ”’ </summary>

    ”’ <param name=”pTrackBarAdministrador”>Recibe una instancia de la clase

    ”’ que administra el evento a producirse al cambiar el valor del

    ”’ TrackBar</param>

    ”’ <remarks>Se agrega al método local OnTrackBarChange como manipulador

    ”’ del evento de tipo Delegate declarado en la clase TrackBarAdministrador.</remarks>

    Public Sub New(ByVal pTrackBarAdministrador As TrackBarAdministrador)

        ‘MyBase.New()

        Me.mTrackBarAdministrador = pTrackBarAdministrador

        AddHandler mTrackBarAdministrador.OnTrackBarChangeHandler, AddressOf Me.OnTrackBarChange

    End Sub

 

    ”’ <summary>

    ”’ Método que manipula al evento a producirse al variar el valor del TrackBar.

    ”’ </summary>

    ”’ <param name=”source”>Parámetro de entrada de tipo Object</param>

    ”’ <param name=”e”>Parámetro de entrada de tipo TrackBarChangeEventsArgs</param>

    ”’ <remarks>Es importante destacar que los parámetros de este método deben ser

    ”’ idénticos a los de la firma del evento de tipo Delegate declarado en la clase

    ”’ TrackBarAdministrador.</remarks>

    Private Sub OnTrackBarChange(ByVal source As Object, ByVal e As TrackBarChangeEventsArgs)

        Form2.txtValor.Text = e.Change

        Form3.txtValor.Text = e.Change

    End Sub

End Class

 

[C#]

using System;

 

/// <summary>

/// Informa a los formularios sobre el evento que administra TrackBarAdministrador.

/// </summary>

/// <remarks></remarks>

class TrackBarObservador

{

    Form2 miForm2 = new Form2();

    Form3 miForm3 = new Form3();

 

    /// <summary>

    /// Variable local del tipo de la clase que administra el evento

    /// a producirse al cambiar el vaor del TrackBar.

    /// </summary>

    /// <remarks></remarks>

    private TrackBarAdministrador mTrackBarAdministrador;

 

    /// <summary>

    /// Constructor

    /// </summary>

    /// <param name=”pTrackBarAdministrador”>Recibe una instancia de la clase

    /// que administra el evento a producirse al cambiar el valor del

    /// TrackBar</param>

    /// <remarks>Se agrega al método local OnTrackBarChange como manipulador

    /// del evento de tipo Delegate declarado en la clase TrackBarAdministrador.</remarks>

    public TrackBarObservador(TrackBarAdministrador pTrackBarAdministrador)

    {

        this.miForm2.Show();

        this.miForm3.Show();

        this.mTrackBarAdministrador = pTrackBarAdministrador;

        mTrackBarAdministrador.OnTrackBarChangeHandler += new TrackBarAdministrador.TrackBarChangeEventHandler(this.OnTrackBarChange);

    }

 

    /// <summary>

    /// Método que manipula al evento a producirse al variar el valor del TrackBar.

    /// </summary>

    /// <param name=”source”>Parámetro de entrada de tipo Object</param>

    /// <param name=”e”>Parámetro de entrada de tipo TrackBarChangeEventsArgs</param>

    /// <remarks>Es importante destacar que los parámetros de este método deben ser

    /// idénticos a los de la firma del evento de tipo Delegate declarado en la clase

    /// TrackBarAdministrador.</remarks>

    private void OnTrackBarChange(object source, TrackBarChangeEventsArgs e)

    {

        this.miForm2.txtValor.Text = e.Change.ToString();

        this.miForm3.txtValor.Text = e.Change.ToString();

    }

}

Pasamos ahora al Form1 que es el que controla la aplicación. Este formulario en su componente visual contiene un TrackBar. En él se declaran una variable local de tipo de la clase TrackBarAdministrador, otra más del tipo de la clase TrackBarObservador y se le pasa la variable de tipo TrackBarAdministrador, recién declarada, como parámetro.

En el evento Scroll del TrackBar, es el que se dispara al cambiar manualmente su valor, ejecutamos el método ChangeTrackBar declarado en la clase TrackBarAdministrador que es el encargado de disparar el evento OnTrackBarChangeHandler y se cierra el círculo.

[VB]

”’ <summary>

”’ Desde este formulario se controlan los valores de Título y

”’ TextBox de los otros dos.

”’ </summary>

”’ <remarks></remarks>

Public Class Form1

    ”’ <summary>

    ”’ Declaro una variable local del tipo TrackBarAdministrador. Luego otra del

    ”’ tipo TrackBarObservador, a la que se le pasa TrackBarAdministrador como

    ”’ parámetro de entrada.

    ”’ </summary>

    ”’ <remarks></remarks>

    Private mTrackBarAdministrador As TrackBarAdministrador = New TrackBarAdministrador

    Private mTrackBarObservador As TrackBarObservador = New TrackBarObservador(mTrackBarAdministrador)

 

    ”’ <summary>

    ”’ Muestro los formularios que se recibirán al evento desde la clase

    ”’ TrackBarObservador.

    ”’ </summary>

    ”’ <param name=”sender”></param>

    ”’ <param name=”e”></param>

    ”’ <remarks></remarks>

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        Form2.Show()

        Form3.Show()

    End Sub

 

    ”’ <summary>

    ”’ Al modificarse el valor del TrackBar se dispara el evento ChangeTrackBar

    ”’ declarado en TrackBarAdministrador.

    ”’ </summary>

    ”’ <param name=”sender”></param>

    ”’ <param name=”e”></param>

    ”’ <remarks></remarks>

    Private Sub TrackBar_Scroll(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TrackBar.Scroll

        mTrackBarAdministrador.ChangeTrackBar(sender.Value)

    End Sub

End Class

[C#]

using System;

using System.Windows.Forms;

 

/// <summary>

/// Desde este formulario se controlan los valores de Título y

/// TextBox de los otros dos.

/// </summary>

public partial class Form1 : Form

{

    public Form1()

    {

        InitializeComponent();

    }

 

    /// <summary>

    /// Declaro una variable local del tipo TrackBarAdministrador. Luego otra del

    /// tipo TrackBarObservador, a la que se le pasa TrackBarAdministrador como

    /// parámetro de entrada.

    /// </summary>

    static TrackBarAdministrador mTrackBarAdministrador = new TrackBarAdministrador();

    TrackBarObservador mTrackBarObservador = new TrackBarObservador(mTrackBarAdministrador);

 

    /// <summary>

    /// Muestro los formularios que se recibirán al evento desde la clase

    /// TrackBarObservador.

    /// </summary>

    /// <param name=”sender”></param>

    /// <param name=”e”></param>

    /// <remarks></remarks>

    private void Form1_Load(object sender, System.EventArgs e)

    {

        Application.Run(new Form2());

        Application.Run(new Form3());

    }

 

    /// <summary>

    /// Al modificarse el valor del TrackBar se dispara el evento ChangeTrackBar

    /// declarado en TrackBarAdministrador.

    /// </summary>

    /// <param name=”sender”></param>

    /// <param name=”e”></param>

    /// <remarks></remarks>

    private void trackBar_Scroll(object sender, System.EventArgs e)

    {

        mTrackBarAdministrador.ChangeTrackBar(((System.Windows.Forms.TrackBar)(sender)).Value);

    }

}

No publico el código de los otros dos formularios porque no contienen más código que el necesario para ser formularios y contener un textBox de nombre txtValor, pueden verificarlo en los ejemplos que les dejo acá para bajar y probar.

Con la idea de haber sido útil, me despido hasta la próxima. 🙂

Hay situaciones en programación que no son de resolución trivial.

Supongamos que tenemos instanciado un objeto servidor y necesitamos conocer algún cambio de estado en el mismo.

Podríamos solucionarlo con un Timer, pero como solución es excesivamente costosa e impráctica. Hay quien sencillamente programa una función en el objeto servidor que avisa al cliente, un horror, desde el punto de vista de la orientación a objetos, impide la reutilización de código, etc.

Para este tipo de situaciones existen salidas más elegantes y efectivas que es lo que vamos a ver en esta nota.

Los delegados son algo similar a los punteros a funciones en C++, pero como corren sobre el Framework de .NET se trata de código administrado (code managed) y de tipo seguro.

Se usan para implementar métodos de devolución de llamada, callbacks, que son fundamentales para lograr:programar procesamiento asíncrono o para insertar código de cliente, entre las instancias que produce el servidor, para no usar timers donde no sea necesario.

Los métodos de devolución de llamada se usan en procesamiento asíncrono porque se desconoce el tiempo que va a tardar en ejecutarse el método que se invoca y que potencialmente podría ser largo.

En este caso vamos a ver como se usan delegados para insertar código de cliente, entre las devoluciones de llamada que produce el servidor.

Para entender esto, vamos a pensar en una aplicación que toma mediciones que pueden tardar en completarse, para el ejemplo no nos importa la medición, por eso la vamos a implementar como una medición simulada en una clase de nombre Measures. Tendremos además otra clase, Manager, que administra las mediciones y una aplicación de consola de nombre App.

Veamos primero la clase Measure: 

[C#]

using System; class Measure{

     /// <summary>

     /// Variable protegida que encapsula el valor de la propiedad Name.

    /// </summary>

     /// <remarks></remarks>

     protected string Name;

     /// <summary>

     /// Constructor de la clase que implementa una Medición simulada.

     /// </summary>

     /// <param name=”name”>Nombre concreto de la Medición.</param>

     /// <remarks>Es una forma de diferenciar las distintas instacias

    /// que se produzcan de esta clase.</remarks>

     public Measure(string name)

    {

        this.name = name;

    }

    /// <summary>

    /// Propiedad Name.

    /// </summary>

    /// <value></value>

    /// <returns>Nombre (Name) de la instancia.</returns>

    /// <remarks>Las últimas dos lineas del get simulan una demora en la

    /// toma de la medición, pongo un beep para que sea más perceptible la

    /// demora entre callbacks. Esto para poder apreciar mejor que se está

    /// haciedo un callback por cada medición, de otra forma el tipo de output de

    /// consola no da idea de cómo se ejecuta el código.</remarks>

    public string name

    {

        get

        {

            for (int i = 0; i <= 100000000; i++) { }            System.Media.SystemSounds.Beep.Play();            return this.Name;        }

        set

        {

            this.Name = value;

        }

    }

} 

[VB]

Imports System 

Public Class Measure

    ”’ <summary>

    ”’ Variable protegida que encapsula el valor de la propiedad Name.

    ”’ </summary>

    ”’ <remarks></remarks>

    Protected strName As String

    ”’ <summary>

    ”’ Constructor de la clase que implementa una Medición simulada.

    ”’ </summary>

    ”’ <param name=”name”>Nombre concreto de la Medición.</param>

    ”’ <remarks>Es una forma de diferenciar las distintas instacias

    ”’ que se produzcan de esta clase.</remarks>

    Public Sub New(ByVal name As String)

        MyBase.New()

        Me.name = name

    End Sub

    ”’ <summary>

    ”’ Propiedad Name.

    ”’ </summary>

    ”’ <value></value>

    ”’ <returns>Nombre (Name) de la instancia.</returns>

    ”’ <remarks>Las últimas tres lineas del Get simulan una demora en la

    ”’ toma de la medición, pongo un beep para que sea más perceptible la

    ”’ demora entre callbacks. Esto para poder apreciar mejor que se está

    ”’ haciedo un callback por cada medición, de otra forma el tipo de output de

    ”’ consola no da idea de cómo se ejecuta el código.</remarks>

    Public Property name() As String

        Get

            Dim i As Int64            For i = 0 To 100000000 : Next            Beep()            Return strName

        End Get

        Set(ByVal value As String)

            strName = value

        End Set

    End Property

End Class 

 

Seguimos ahora con la clase Manager: 

[C#]

using System; 

class Manager{

    /// <summary>

    /// Es una constante donde se define la cantidad de

    /// mediciones simuladas que se harán.

    /// </summary>

    /// <remarks></remarks>

    int kMeasure = 4;

    /// <summary>

    /// Arreglo de Measures que soporta las mediciones.

    /// </summary>

    /// <remarks></remarks>

    static Measure[] Measures;

    /// <summary>

    /// Definición de la función Delegada

    /// </summary>

    /// <param name=”pMeasure”>Recibe un objeto Measure como entrada</param>

    /// <remarks>Esta es la función sobre la que se implementa el método

    /// de devolución de llamada (callback) al nombre de esta función se

    /// lo postfija con Callback por convención con la idea de facilitar

    /// la lectura.</remarks>

    public delegate void EnumMeasuresCallback(Measure measure);

    /// <summary>

    /// Agrega mediciones simuladas.

    /// </summary>

    /// <remarks>Se agregan tantas como hayan definido en kMeasure.</remarks>

    public void AddMeasures()

    {

        Measures = new Measure[kMeasure];

        for (System.Int64 i = 0; i < kMeasure; i++)

        {

            Measures[i] = new Measure(“Measure “ + (i + 1));

        }

    }

    /// <summary>

    /// Enumera mediciones, es la función que se invocará desde la aplicación.

    /// </summary>

    /// <param name=”callback”>Función delegada.</param>

    /// <remarks>Aceptando una función delegada como parámetro de entrada

    /// es la forma en que en que queda establecido efectivamente el callback.</remarks>

    public static void EnumMeasures(EnumMeasuresCallback callback)

    {

        foreach (Measure measure in Measures)

        {

            callback(measure);

        }

    }

}  

[VB]

Imports System 

Public Class Manager

    ”’ <summary>

    ”’ Es una constante donde se define la cantidad de

    ”’ mediciones simuladas que se harán.

    ”’ </summary>

    ”’ <remarks></remarks>

    Private Const kMeasure As Integer = 4

    ”’ <summary>

    ”’ Arreglo de Measures que soporta las mediciones.

    ”’ </summary>

    ”’ <remarks></remarks>

    Private Shared Measures(kMeasure) As Measure

    ”’ <summary>

    ”’ Definición de la función Delegada

    ”’ </summary>

    ”’ <param name=”pMeasure”>Recibe un objeto Measure como entrada</param>

    ”’ <remarks>Esta es la función sobre la que se implementa el método

    ”’ de devolución de llamada (callback) al nombre de esta función se

    ”’ lo postfija con Callback por convención con la idea de facilitar

    ”’ la lectura.</remarks>

    Public Delegate Sub EnumMeasuresCallback(ByVal pMeasure As Measure)

    ”’ <summary>

    ”’ Agrega mediciones simuladas.

    ”’ </summary>

    ”’ <remarks>Se agregan tantas como hayan definido en kMeasure.</remarks>

    Public Sub AddMeasures()

        Dim i As Integer

        For i = 0 To kMeasure

            Measures(i) = New Measure((“Measure “ + CStr(i + 1)))

        Next i

    End Sub

    ”’ <summary>

    ”’ Enumera mediciones, es la función que se invocará desde la aplicación.

    ”’ </summary>

    ”’ <param name=”callback”>Función delegada.</param>

    ”’ <remarks>Aceptando una función delegada como parámetro de entrada

    ”’ es la forma en que en que queda establecido efectivamente el callback.</remarks>

    Public Shared Sub EnumMeasures(ByVal callback As EnumMeasuresCallback)

        For Each mMeasure As Measure In Measures

            callback(mMeasure)

        Next

    End Sub

End Class 

 

Las lineas más destacables de Manager son  

[C#]

public delegate void EnumMeasuresCallback(Measure measure);

[VB]

Public Delegate Sub EnumMeasuresCallback(ByVal pMeasure As Measure)

Esta es la definición de la función sobre la que se implementará el callback 

[C#]

    public static void EnumMeasures(EnumMeasuresCallback callback)

    {

        foreach (Measure measure in Measures)

        {

            callback(measure);

        }

    }

[VB]

    Public Shared Sub EnumMeasures(ByVal callback As EnumMeasuresCallback)

        For Each mMeasure As Measure In Measures

            callback(mMeasure)

        Next

    End Sub

Esta es la función que se invocará desde la aplicación (que todavía no vimos), su característica más saliente es que recibe una función delegada como parámetro de entrada. 

Por último la aplicación de consola: 

[C#]

using System;

/// <summary>

/// Esta es la aplicación desde dónde compruebo el funcionamiento de los callbacks.

/// </summary>

/// <remarks>Esta primera línea de código

/// Public Shared miCallback As Manager.EnumMeasuresCallback = New Manager.EnumMeasuresCallback(AddressOf MeasuresCallback)

/// En esta definición es dónde efectivamente relaciono la función delegada

/// con una función de la aplicación. En otras palabras, delego la función

/// EnumMeasuresCallback (definida en Manager) en MeasuresCallback (definida

/// localmente). En esta misma linea estoy definiendo la varibal miCallback,

/// de tipo Delegate, implícito en Manager.EnumMeasuresCallback.</remarks>

class App{

    public static Manager.EnumMeasuresCallback miCallback = new Manager.EnumMeasuresCallback(MeasuresCallback);

    /// <summary>

    /// Acá se inserta el código que se quiere ejecutar por cada callback.

    /// </summary>

    /// <param name=”pMeasure”></param>

    /// <remarks></remarks>

    public static void MeasuresCallback(Measure measure)

    {

        Console.WriteLine(“Callback “ + measure.name);

    }

    /// <summary>

    /// Rutina principal de la aplicación

    /// </summary>

    /// <remarks>Con mgr.AddMeasures(), agrego las mediciones simuladas.

    /// En la invocación

    /// Manager.EnumMeasures(miCallback)

    /// está la llamada a la función que implementa el callback.

    /// En la clase Manager podrá verse que es la función

    /// que recibe un Delegate como parámetro de entrada.

    /// Es así que le paso el delegado que definí más arriba

    /// con nombre miCallback.

    /// </remarks>

    public static void Main()

    {

        Manager mgr = new Manager();

        Console.WriteLine(“Agrego mediciones”);

        mgr.AddMeasures();

        Console.WriteLine(“Entra”);

        Manager.EnumMeasures(miCallback);

        Console.WriteLine(“Sale”);

        Console.ReadLine();

    }

} 

[VB]

Imports System 

”’ <summary>

”’ Esta es la aplicación desde dónde compruebo el funcionamiento de los callbacks.

”’ </summary>

”’ <remarks>Esta primera línea de código

”’ Public Shared miCallback As Manager.EnumMeasuresCallback = New Manager.EnumMeasuresCallback(AddressOf MeasuresCallback)

”’ En esta definición es dónde efectivamente relaciono la función delegada

”’ con una función de la aplicación. En otras palabras, delego la función

”’ EnumMeasuresCallback (definida en Manager) en MeasuresCallback (definida

”’ localmente). En esta misma linea estoy definiendo la varibal miCallback,

”’ de tipo Delegate, implícito en Manager.EnumMeasuresCallback.</remarks>

Class App

    Public Shared miCallback As Manager.EnumMeasuresCallback = New Manager.EnumMeasuresCallback(AddressOf MeasuresCallback)

    ”’ <summary>

    ”’ Acá se inserta el código que se quiere ejecutar por cada callback.

    ”’ </summary>

    ”’ <param name=”pMeasure”></param>

    ”’ <remarks></remarks>

    Public Shared Sub MeasuresCallback(ByVal pMeasure As Measure)

        Console.WriteLine((“Callback “ + pMeasure.name))

    End Sub

    ”’ <summary>

    ”’ Rutina principal de la aplicación

    ”’ </summary>

    ”’ <remarks>Con mgr.AddMeasures(), agrego las mediciones simuladas.

    ”’ En la invocación

    ”’ Manager.EnumMeasures(miCallback)

    ”’ está la llamada a la función que implementa el callback.

    ”’ En la clase Manager podrá verse que es la función

    ”’ que recibe un Delegate como parámetro de entrada.

    ”’ Es así que le paso el delegado que definí más arriba

    ”’ con nombre miCallback.

    ”’ </remarks>

    Public Shared Sub Main()

        Dim mgr As Manager = New Manager

        Console.WriteLine(“Agrego Mediciones”)

        mgr.AddMeasures()

        Console.WriteLine(“Entra”)

        Manager.EnumMeasures(miCallback)

        Console.WriteLine(“Sale”)

        Console.ReadLine()

    End Sub

End Class 

 

Lo fundamental en la aplicación son las siguientes líneas de código: 

[C#]

public static Manager.EnumMeasuresCallback miCallback = new Manager.EnumMeasuresCallback(MeasuresCallback);

[VB]

Public Shared miCallback As Manager.EnumMeasuresCallback = New Manager.EnumMeasuresCallback(AddressOf MeasuresCallback)

Acá se declara el delegado (miCallback) que se pasará como parámetro de entrada a la función EnumMeasures que implementa el callback en Manager. Es importante notar que la función de devolución de llamada recibe como parámetro de entrada la dirección de la función delegada 

[C#]

Manager.EnumMeasures(miCallback);

[VB]

Manager.EnumMeasures(miCallback)

En estas líneas estamos invocando a la función que implementa el callback, para eso se le pasa cómo parámetro el delegado definico al comienzo.  

[C#]

    public static void MeasuresCallback(Measure measure)

    {

        Console.WriteLine(“Callback “ + measure.name);

    }

[VB]

    Public Shared Sub MeasuresCallback(ByVal pMeasure As Measure)

        Console.WriteLine((“Callback “ + pMeasure.name))

    End Sub

Esta es la función delegada. En otras palabras en ella se delega el Delegate, como se define en la declaración de miCallback. 

Este es sólo un ejemplo que usa Delegates, hay mucho más sobre este tema.

La solución con los proyectos de ejemplo se pueden descargar de Delegates.zip.

Para quien quiera profundizar es recomendable cualquier libro, en especial el libro de Tom Archer que conocí gracias a la desinteresada recomendación de Daniel Calvin. 

ISBN: 84-481-3246-7

Autor: Tom Archer (http://blogs.msdn.com/tomarcher/)

Título: C# a fondo (http://www.cuspide.com/isbn/8448132467)
Editorial: Mc Graw Hill. 

 

Les mando un saludo. 🙂

Asesoramiento a Organizaciones Públicas y Privadas.

Cursos a grupos ya constituidos.

Charlas de orientación.

Metodologías Ágiles.

Startups.

Visitas

  • 135,866 hits

Mejor calificado

del.icio.us