Qué es Func, cómo y cuándo se usa

¿Qué es Func y para qué se utiliza?

Func es un tipo de delegado predefinido para un método que devuelve algún valor del tipo T

En otras palabras, puede usar este tipo para hacer referencia a un método que devuelve algún valor de T P.ej

 public static string GetMessage() { return "Hello world"; } 

se puede hacer referencia de esta manera

 Func f = GetMessage; 

Piense en ello como un marcador de posición. Puede ser bastante útil cuando tiene un código que sigue un cierto patrón, pero no necesita estar vinculado a ninguna funcionalidad en particular.

Por ejemplo, considere el método de extensión Enumerable.Select .

  • El patrón es: para cada elemento de una secuencia, seleccione algún valor de ese elemento (por ejemplo, una propiedad) y cree una nueva secuencia que consista en estos valores.
  • El marcador de posición es: alguna función de selector que realmente obtiene los valores para la secuencia descrita anteriormente.

Este método toma un Func lugar de cualquier función concreta. Esto permite que se use en cualquier contexto donde se aplique el patrón anterior.

Entonces, por ejemplo, digamos que tengo una List y solo quiero el nombre de cada persona en la lista. Puedo hacer esto:

 var names = people.Select(p => p.Name); 

O di que quiero la edad de cada persona:

 var ages = people.Select(p => p.Age); 

De inmediato, puede ver cómo pude aprovechar el mismo código que representa un patrón (con Select ) con dos funciones diferentes ( p => p.Name y p => p.Age ).

La alternativa sería escribir una versión diferente de Select cada vez que desee escanear una secuencia para un tipo diferente de valor. Entonces, para lograr el mismo efecto que el anterior, necesitaría:

 // Presumbly, the code inside these two methods would look almost identical; // the only difference would be the part that actually selects a value // based on a Person. var names = GetPersonNames(people); var ages = GetPersonAges(people); 

Con un delegado actuando como marcador de posición, me libero de tener que escribir el mismo patrón una y otra vez en casos como este.

Func representa una función, que toma argumentos (T1, T2, …, Tn) y devuelve Tr.

Por ejemplo, si tienes una función:

 double sqr(double x) { return x * x; } 

Puede guardarlo como una especie de variable de función:

 Func f1 = sqr; Func f2 = x => x * x; 

Y luego use exactamente como usaría sqr:

 f1(2); Console.WriteLine(f2(f1(4))); 

etc.

Recuerde, sin embargo, que es un delegado, para obtener información más avanzada, consulte la documentación.

Func y los otros delegates de Func generics predefinidos ( Func , Func y otros) son delegates generics que devuelven el tipo del último parámetro genérico.

Si tiene una función que necesita devolver diferentes tipos, dependiendo de los parámetros, puede usar un delegado de Func , especificando el tipo de devolución.

Es solo un delegado genérico predefinido. Al usarlo no necesita declarar a cada delegado. Hay otro delegado predefinido, Action , que es el mismo pero devuelve vacío.

Encuentro Func muy útil cuando creo un componente que necesita ser personalizado “sobre la marcha”.

Tome este ejemplo muy simple: un PrintListToConsole .

Un objeto muy simple que imprime esta lista de objetos en la consola. Desea que el desarrollador que lo utiliza personalice la salida.

Por ejemplo, quiere dejar que defina un tipo particular de formato numérico, etc.

Sin Func

Primero, debe crear una interfaz para una clase que toma la entrada y produce la cadena para imprimir en la consola.

 interface PrintListConsoleRender { String Render(T input); } 

Luego debe crear la clase PrintListToConsole que toma la interfaz creada anteriormente y la usa sobre cada elemento de la lista.

 class PrintListToConsole { private PrintListConsoleRender _renderer; public void SetRenderer(PrintListConsoleRender r) { // this is the point where I can personalize the render mechanism _renderer = r; } public void PrintToConsole(List list) { foreach (var item in list) { Console.Write(_renderer.Render(item)); } } } 

El desarrollador que necesita usar su componente tiene que:

  1. implementar la interfaz

  2. pasar la clase real a la PrintListToConsole

     class MyRenderer : PrintListConsoleRender { public String Render(int input) { return "Number: " + input; } } class Program { static void Main(string[] args) { var list = new List { 1, 2, 3 }; var printer = new PrintListToConsole(); printer.SetRenderer(new MyRenderer()); printer.PrintToConsole(list); string result = Console.ReadLine(); } } 

Usar Func es mucho más simple

Dentro del componente define un parámetro de tipo Func que representa una interfaz de una función que toma un parámetro de entrada de tipo T y devuelve una cadena (la salida para la consola)

 class PrintListToConsole { private Func _renderFunc; public void SetRenderFunc(Func r) { // this is the point where I can set the render mechanism _renderFunc = r; } public void Print(List list) { foreach (var item in list) { Console.Write(_renderFunc(item)); } } } 

Cuando el desarrollador usa su componente, simplemente pasa al componente la implementación del tipo Func , que es una función que crea la salida para la consola.

 class Program { static void Main(string[] args) { var list = new Array[1, 2, 3]; var printer = new PrintListToConsole(); printer.SetRenderFunc((o) => "Number:" + o); printer.Print(); string result = Console.ReadLine(); } } 

Func permite definir una interfaz de método genérico sobre la marcha. Usted define qué tipo es la entrada y qué tipo es la salida. Simple y conciso.