Explicación de Func

89

Me preguntaba si alguien podría explicar qué Func<int, string>es y cómo se usa con algunos ejemplos claros.

zSinopsis
fuente

Respuestas:

145

¿Conoce a los delegados en general? Tengo una página sobre delegados y eventos que puede ayudar si no, aunque está más orientada a explicar las diferencias entre los dos.

Func<T, TResult>es solo un delegado genérico: averigüe lo que significa en cualquier situación particular reemplazando los parámetros de tipo ( Ty TResult) con los argumentos de tipo correspondientes ( inty string) en la declaración. También lo renombré para evitar confusiones:

string ExpandedFunc(int x)

En otras palabras, Func<int, string>es un delegado que representa una función que toma un intargumento y devuelve un string.

Func<T, TResult>se usa a menudo en LINQ, tanto para proyecciones como para predicados (en el último caso, TResultes siempre bool). Por ejemplo, puede usar a Func<int, string>para proyectar una secuencia de números enteros en una secuencia de cadenas. Las expresiones lambda generalmente se usan en LINQ para crear los delegados relevantes:

Func<int, string> projection = x => "Value=" + x;
int[] values = { 3, 7, 10 };
var strings = values.Select(projection);

foreach (string s in strings)
{
    Console.WriteLine(s);
}

Resultado:

Value=3
Value=7
Value=10
Jon Skeet
fuente
3
"En otras palabras, es un delegado que representa una función que toma un argumento int y devuelve una cadena". Solo para evitar confusiones para los demás, aclararé que estás hablando de Func <int, string> aquí y no de Func <T, TResult>. Es obvio si comprende los tipos genéricos y los delegados, pero para aquellos que no lo hacen, es Func <int, string> que puede delegar a una función que toma un argumento int y devuelve una cadena.
The real napster
Aclarará cuando vuelva a una PC más tarde.
Jon Skeet
2
Creo que esto en realidad no es tan claro como la descripción y el ejemplo de MSDN. También creo que debería agregar información sobre cómo el último parámetro de tipo es el tipo de retorno, aclarando que Func <int, int, string> devuelve una cadena y toma 2 entradas. Eso ayuda a aclarar. Nada personal, simplemente no pensé que fuera lo suficientemente claro.
TheSoftwareJedi
11
Entonces, ¿vas a rechazar todas las respuestas que consideres que no son tan útiles como tu favorita en particular? ¿ Cree que esta respuesta es activamente inútil ? ¿Crees que quizás tener más de una forma de ver las cosas no sea una mala idea?
Jon Skeet
8
@TheSoftwareJedi: No, por supuesto, no hay razón para tomar su downvote personalmente - el hecho de que se hizo downvote por razones personales el sábado y luego simplemente pasó a venir a este hilo después de que hemos tenido una larga discusión sobre correo electrónico sobre el comportamiento apropiado es completamente una coincidencia, ¿no?
Jon Skeet
40

A Func<int, string>come entradas y devuelve cadenas. Entonces, ¿qué come ints y devuelve cadenas? Qué tal esto ...

public string IntAsString( int i )
{
  return i.ToString();
}

Allí, acabo de inventar una función que come entradas y devuelve cadenas. ¿Cómo lo usaría?

var lst = new List<int>() { 1, 2, 3, 4, 5 };
string str = String.Empty;

foreach( int i in lst )
{
  str += IntAsString(i);
}

// str will be "12345"

No muy sexy, lo sé, pero esa es la simple idea en la que se basan muchos trucos. Ahora, usemos un Func en su lugar.

Func<int, string> fnc = IntAsString;

foreach (int i in lst)
{
  str += fnc(i);
}

// str will be "1234512345" assuming we have same str as before

En lugar de llamar a IntAsString en cada miembro, creé una referencia llamada fnc (estas referencias a métodos se llaman delegados ) y la usé en su lugar. (Recuerde que fnc come ints y devuelve cadenas).

Este ejemplo no es muy atractivo, pero muchas de las cosas inteligentes que verá se basan en la simple idea de funciones, delegados y métodos de extensión .

Uno de los mejores manuales sobre este tema que he visto está aquí . Tiene muchos más ejemplos reales. :)

JP Alioto
fuente
@Therealnapster también me gusta, pero me gusta más tu nombre.
BKSpurgeon
27

Es un delegado que toma uno intcomo parámetro y devuelve un valor de tipo string.

A continuación, se muestra un ejemplo de su uso:

using System;

class Program
{
    static void Main()
    {
        Func<Int32, String> func = bar;

        // now I have a delegate which 
        // I can invoke or pass to other
        // methods.
        func(1);
    }

    static String bar(Int32 value)
    {
        return value.ToString();
    }
}
Andrew Hare
fuente
3
Gracias andrew. ¿Quería escribir func (1) en lugar de bar (1)?
zSynopsis
1

Func<int, string>acepta un parámetro de valor int y devuelve un valor de cadena. Aquí hay un ejemplo en el que no es necesario un método de apoyo adicional.

Func<int, string> GetDogMessage = dogAge =>
        {
            if (dogAge < 3) return "You have a puppy!";
            if (dogAge < 7) return "Strong adult dog!";

            return "Age is catching up with the dog!";
        };

string youngDogMessage = GetDogMessage(2);

NOTA: El último tipo de objeto en Func (es decir, "cadena" en este ejemplo) es el tipo de retorno de funciones (es decir, no se limita a primitivas, sino a cualquier objeto). Por lo tanto, Func<int, bool, float>acepta los parámetros de valor int y bool y devuelve un valor flotante.

Func<int, bool, float> WorthlessFunc = (intValue, boolValue) =>
        {
            if(intValue > 100 && boolValue) return 100;

            return 1;
        };
float willReturn1 = WorthlessFunc(21, false);
float willReturn100 = WorthlessFunc(1000, true);

HTH

jts
fuente