Por eso me he decidido a hacer este post dedicado a funciones delegadas para tener claro que estamos manejando en cada momento.
Una función delegada consiste en un puntero a función, esto es un mecanismo muy estudiado en todos los lenguajes de programación y casi todos lo implementan. Más concretamente en el paradigma de la programación funcional, es un concepto muy básico, la idea de pasar funciones a través de variables. Habitualmente manejamos información que ya está procesada, números, cadenas de texto, etc, pero es menos habitual que en lugar de pasar información, pasemos como procesar la información.
Veamos un ejemplo clásico de funciones delegadas:
class Program { delegate int FuncPointer(int x,int y); static void Main(string[] args) { invokePrint(sum, 8, 5); invokePrint(sub, 8, 5); Console.ReadLine(); } static int sum(int x,int y) { return x+y; } static int sub(int x,int y) { return x-y; } static void invokePrint(FuncPointer caller, int param1, int param2) { int result=caller.Invoke(param1, param2); Console.WriteLine(result); } }En este ejemplo tenemos dos funciones que calculan la suma y la resta de un numero. A parte la función invokePrint se encarga de ejecutar una función que recibe por parámetro y mostrar el resultado que devuelve por pantalla.
La firma de las funciones sum y sub coinciden con la definición de la delegada FuncPointer, lo cual nos permite llamar a invokePrint con las funciones como parámetro.
Uso de delegados genéricos Func
Podemos definir un delegado genérico utilizando Func, eso nos da una gran versatilidad en el código pues nos permite utilizar delegados sin declarar tipos, como por ejemplo en el caso anterior tuvimos que declarar el tipo FuncPointer.
Vamos a repetir el caso anterior usando delegados genéricos y expresiones lambda:
class Program { delegate int FuncPointer(int x,int y); static void Main(string[] args) { Func<int, int int> sum = (x, y) => x + y; Func<int, int int> sub = (x, y) => x - y; invokePrint(sum, 8, 5); invokePrint(sub, 8, 5); Console.ReadLine(); } static void invokePrint(Func<int,int,int> caller, int param1, int param2) { int result = caller(param1, param2); Console.WriteLine(result); } }
Como podemos ver no hemos fijado un delegado, sino que pasamos un delegado genérico. Trabajar de esta forma es mucho más cómodo, pues no es necesario que otras clases tengan visibilidad del tipo delegado, en el parámetro se define la propia firma. Para definir un delegado genérico invocamos a Func con los tipos de los parámetros:
Func<TipoParamEntrada1, TipoParamEntrada2, ... TipoParamEntradaN, TipoSalida> myDelegate;
Action y Predicate
Para acabar el articulo quiero mencionar estos dos objetos que permiten al igual que Func definir delegados genéricos.Action define un delegado que tiene parametros de entrada pero no de salida. Correspondería a un Func<tipo,....,void>
Action<string> write = x => Console.Write(x);
Esta acción mostraría por consola el texto introducido. Como vemos tiene un parámetro de entrada pero no tiene de salida. Este objeto casa muy bien con procedimientos que no devuelve valor.
Predicate define un delegado de tipo Booleano, equivale a Func<Tipo,Boolean>
Predicate<string> checkPwd = x => x=="password";
Y con esto concluyo esta breve introducción a los punteros de funciones que siempre resultan muy útiles. Espero que os sea de ayuda.
No hay comentarios:
Publicar un comentario