Wyjaśnienie funkcji Func

89

Zastanawiałem się, czy ktoś mógłby wyjaśnić, co to Func<int, string>jest i jak jest używane, z jasnymi przykładami.

zSynopsis
źródło

Odpowiedzi:

145

Czy ogólnie znasz delegatów? Mam stronę o delegatach i wydarzeniach, która może pomóc, jeśli nie, chociaż jest bardziej nastawiona na wyjaśnienie różnic między nimi.

Func<T, TResult>jest tylko delegatem ogólnym - dowiedz się, co to oznacza w dowolnej konkretnej sytuacji, zastępując parametry typu ( Ti TResult) odpowiednimi argumentami typu ( inti string) w deklaracji. Zmieniłem też jego nazwę, aby uniknąć nieporozumień:

string ExpandedFunc(int x)

Innymi słowy, Func<int, string>jest delegatem, który reprezentuje funkcję pobierającą intargument i zwracającą string.

Func<T, TResult>jest często używany w LINQ, zarówno dla prognoz, jak i predykatów (w tym drugim przypadku TResultjest zawsze bool). Na przykład można użyć a, Func<int, string>aby zaprojektować sekwencję liczb całkowitych na sekwencję ciągów. Wyrażenia lambda są zwykle używane w LINQ do tworzenia odpowiednich delegatów:

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);
}

Wynik:

Value=3
Value=7
Value=10
Jon Skeet
źródło
3
„Innymi słowy, jest to delegat, który reprezentuje funkcję pobierającą argument int i zwracającą ciąg”. Aby uniknąć nieporozumień dla innych, wyjaśnię, że mówisz tutaj o Func <int, string>, a nie o Func <T, TResult>. Jest oczywiste, jeśli rozumiesz typy ogólne i delegatów, ale dla tych, którzy tego nie robią, funkcja Func <int, string> jest delegowana przez kan do funkcji, która przyjmuje argument int i zwraca ciąg.
Prawdziwy napster
Wyjaśni, kiedy wrócę na komputer później.
Jon Skeet
2
Myślę, że to faktycznie nie jest tak jasne, jak opis i przykład MSDN. Myślę również, że powinieneś dodać informacje o tym, jak ostatni parametr typu jest typem zwracanym - wyjaśniając, że Func <int, int, string> zwraca ciąg i przyjmuje 2 ints. To pomaga wyjaśnić. Nic osobistego - po prostu nie sądziłem, że było to wystarczająco jasne.
TheSoftwareJedi
11
Czy zamierzasz więc zlekceważyć każdą odpowiedź, którą uważasz za nie tak pomocną, jak twoja ulubiona? Czy myślisz, że ta odpowiedź jest aktywnie nieprzydatna ? Czy uważasz, że być może posiadanie więcej niż jednego sposobu patrzenia na rzeczy może nie być złym pomysłem?
Jon Skeet
8
@TheSoftwareJedi: Nie, oczywiście, nie ma powodu, aby wziąć downvote osobiście - fakt, że zrobił downvote z powodów osobistych w sobotę, a potem po prostu się przyjść do tego wątku po byliśmy po długiej dyskusji na temat właściwego zachowania email jest całkowicie przypadkowe, prawda?
Jon Skeet
40

A Func<int, string>zjada ints i zwraca stringi. Więc co zjada int i zwraca ciągi? Co powiesz na to ...

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

Tam właśnie stworzyłem funkcję, która zjada ints i zwraca ciągi. Jak bym tego użył?

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"

Wiem, że niezbyt seksowne, ale na tym polega prosta idea, na której opiera się wiele sztuczek. Teraz użyjmy zamiast tego Func.

Func<int, string> fnc = IntAsString;

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

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

Zamiast wywoływać IntAsString na każdym elemencie członkowskim, utworzyłem odwołanie do niego o nazwie fnc (te odwołania do metod nazywane są delegatami ) i zamiast tego użyłem tego. (Pamiętaj, że fnc zjada wartości int i zwraca ciągi znaków).

Ten przykład nie jest zbyt seksowny, ale mnóstwo sprytnych rzeczy, które zobaczysz, opiera się na prostej idei funkcji, delegatów i metod rozszerzających .

Jeden z najlepszych podkładów na ten temat, jaki widziałem, jest tutaj . Ma o wiele więcej prawdziwych przykładów. :)

JP Alioto
źródło
@Therealnapster Ja też to lubię, ale bardziej podoba mi się Twoje imię.
BKSpurgeon,
28

Jest to delegat, który przyjmuje je intjako parametr i zwraca wartość typu string.

Oto przykład jego użycia:

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
źródło
3
Dzięki Andrew. Czy chodziło Ci o napisanie func (1) zamiast bar (1)?
zSynopsis
1

Func<int, string>akceptuje parametr wartości int i zwraca wartość ciągu. Oto przykład, w którym dodatkowa metoda wspomagająca nie jest konieczna.

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);

UWAGA: Ostatnim typem obiektu w Func (tj. „String” w tym przykładzie) jest typ zwracany przez funkcje (tj. Nie jest ograniczony do prymitywów, ale dowolnego obiektu). Dlatego Func<int, bool, float>akceptuje parametry wartości typu int i bool i zwraca wartość zmiennoprzecinkową.

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
źródło