93 - Delegados


Otro concepto que incorpora el lenguaje C# son los delegados que son necesarios en conceptos muy utilizados en C# moderno como son las lambdas y LINQ.

Un delegado almacena la referencia de un método con una estructura definida (en cuanto al tipo de dato que devuelve y los tipos y cantidad de parámetros)

Un ejemplo de sintaxis para declarar un delegado:

    delegate void delegadoA(int x, int y);

Un delegado comienza con la palabra clave delegate. Luego se declara la firma del método (tipo de dato que retorna, los parámetros y el nombre del delegado)

Problema:

Declarar un delegado que reciba dos enteros y retorne un entero.
Plantear una clase Operacion y los métodos que permitan sumar y restar dos enteros.
Llamar a los métodos mediante la definición de un delegado.

Programa:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Delegados1
{
    delegate int Operar(int x1, int x2);

    class Program
    {
        public int Sumar(int x, int y)
        {
            return x + y;
        }

        public int Restar(int x, int y)
        {
            return x - y;
        }

        static void Main(string[] args)
        {
            Program p = new Program();
            Console.WriteLine("Suma y resta de dos valores llamando directamente a los métodos.");
            Console.WriteLine(p.Sumar(10, 5));
            Console.WriteLine(p.Restar(10, 5));
            Operar delegado = p.Sumar;
            Console.WriteLine("Suma y resta de dos valores llamando a los métodos a través de delegados");
            Console.WriteLine(delegado(10, 5));
            delegado = p.Restar;
            Console.WriteLine(delegado(10, 5));
            Console.ReadKey();
        }
    }
}

Declaramos el delegado Operar que recibe dos parámetros de tipo int y retorna un int:

    delegate int Operar(int x1, int x2);

En la clase Program definimos dos métodos Sumar y Restar que tienen una firma idéntica al delegado Operar:

    class Program
    {
        public int Sumar(int x, int y)
        {
            return x + y;
        }

        public int Restar(int x, int y)
        {
            return x - y;
        }

En el método Main definimos un objeto de la clase Program y llamamos directamente sus dos métodos:

        static void Main(string[] args)
        {
            Program p = new Program();
            Console.WriteLine("Suma y resta de dos valores llamando directamente a los métodos.");
            Console.WriteLine(p.Sumar(10, 5));
            Console.WriteLine(p.Restar(10, 5));

Lo nuevo aparece cuando definimos un objeto del tipo delegado Operar y le cargamos la referencia del método Sumar:

            Operar delegado = p.Sumar;

La variable delegado tiene ahora la referencia del método Sumar y podemos llamar a dicho método a través del delegado:

            Console.WriteLine(delegado(10, 5));

Luego modificamos la variable delegado y almacenamos la referencia al método Restar y llamamos a dicho método a través del delegado:

            delegado = p.Restar;
            Console.WriteLine(delegado(10, 5));

En este problema no tiene ninguna ventaja definir un delegado y llamar a los métodos Sumar y Restar a través de delegados. Hemos presentado este problema con el objetivo de comenzar a entender la sintaxis de como se declara un delegado y que almacena una variable de este tipo. A medida que avancemos veremos las ventajas de delegados cuando definamos lambdas y entremos al tema de LINQ.

Parámetros de tipo delegado.

Un método puede recibir como parámetro un delegado y a partir de esto llamar al método que almacena el delegado.

Problema:

Declarar un delegado que reciba dos enteros y retorne un entero.
Plantear una clase Operacion y los métodos que permitan sumar y restar dos enteros. Un tercer método que reciba un delegado y dos enteros.

Programa:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Delegados2
{
    delegate int Operar(int x1, int x2);

    class Program
    {
        public int Sumar(int x, int y)
        {
            return x + y;
        }

        public int Restar(int x, int y)
        {
            return x - y;
        }

        public void operacion(Operar d, int x, int y)
        {
            Console.WriteLine(d(10, 5));
        }

        static void Main(string[] args)
        {
            Program p = new Program();
            Console.WriteLine("Suma y resta de 10 y 5.");
            p.operacion(p.Sumar, 10, 5);
            p.operacion(p.Restar, 10, 5);
            Console.ReadKey();
        }
    }
}

Declaramos el delegado:

    delegate int Operar(int x1, int x2);

Declaramos la clase Program y los dos métodos que suman y restan dos enteros:

    class Program
    {
        public int Sumar(int x, int y)
        {
            return x + y;
        }

        public int Restar(int x, int y)
        {
            return x - y;
        }

Lo nuevo aparece en el método operacion que recibe en el primer parámetro un delegado de tipo Operar y dos enteros. Dentro del algoritmo del método se llama al método que almacena el delegado:

        public void operacion(Operar d, int x, int y)
        {
            Console.WriteLine(d(10, 5));
        }

En la función Main luego de crear un objeto de la clase Program procedemos a llamar solo al método operacion y pasar en el primer parámetro la referencia del método Sumar o Restar según la actividad que necesitamos hacer con los dos enteros restantes:

        static void Main(string[] args)
        {
            Program p = new Program();
            Console.WriteLine("Suma y resta de 10 y 5.");
            p.operacion(p.Sumar, 10, 5);
            p.operacion(p.Restar, 10, 5);
            Console.ReadKey();
        }

Retornar