Co to jest Func, jak i kiedy jest stosowany

Co to jest Func<> i w jakim celu się go stosuje?

Author: CharithJ, 2010-09-02

6 answers

Func<T> jest predefiniowanym typem delegata dla metody, która zwraca jakąś wartość typu T.

Innymi słowy, możesz użyć tego typu, aby odwołać się do metody, która zwraca pewną wartość T. Np.

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

Może być odwołany w ten sposób

Func<string> f = GetMessage;
 63
Author: Brian Rasmussen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-02 07:58:19

Pomyśl o tym jak o zastępstwie. Może to być bardzo przydatne, gdy masz kod, który podąża za określonym wzorcem, ale nie musi być powiązany z żadną konkretną funkcjonalnością.

Na przykład rozważ metodę rozszerzenia Enumerable.Select.

  • wzór jest następujący: dla każdego elementu w sekwencji wybierz jakąś wartość z tego elementu (np. właściwość) i utwórz nową sekwencję składającą się z tych wartości.
  • the placeholder is: some selector function that actually gets wartości dla sekwencji opisanej powyżej.

Ta metoda przyjmuje Func<T, TResult> zamiast jakiejkolwiek konkretnej funkcji. Pozwala to na użycie go w dowolnym kontekście , w którym powyższy wzór ma zastosowanie.

Więc na przykład, powiedzmy, że mam {[5] } i chcę tylko nazwisko każdej osoby na liście. Mogę to zrobić:

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

Lub powiedzmy, że chcę wiek każdej osoby:

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

Od razu widać, jak udało mi się wykorzystać ten sam kod reprezentując wzór (z Select) z dwoma różnymi funkcjami (p => p.Name i p => p.Age).

Alternatywą byłoby napisanie innej wersji Select za każdym razem, gdy chcesz zeskanować sekwencję w poszukiwaniu innego rodzaju wartości. Aby więc osiągnąć taki sam efekt jak powyżej, potrzebowałbym:

// Presumably, 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);

Z delegatem działającym jako zastępczy, uwalniam się od konieczności wypisywania tego samego wzoru w kółko w takich przypadkach.

 74
Author: Dan Tao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-02 08:00:07

Func<T1, T2, ..., Tn, Tr> reprezentuje funkcję, która przyjmuje (T1, T2, ..., Tn) argumenty i zwraca Tr.

Na przykład, jeśli masz funkcję:

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

Możesz zapisać ją jako jakąś zmienną funkcji:

Func<double, double> f1 = sqr;
Func<double, double> f2 = x => x * x;

A następnie użyj dokładnie tak, jak używasz sqr:

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

Itd.

Pamiętaj jednak, że jest to delegat, aby uzyskać bardziej zaawansowane informacje, zapoznaj się z dokumentacją.

 54
Author: Grozz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-02 07:54:44

Func<T1,R> i inne predefiniowane generyczne Func (Func<T1,T2,R>, Func<T1,T2,T3,R> i inne) są delegatami generycznymi, które zwracają Typ ostatniego parametru generycznego.

Jeśli masz funkcję, która musi zwracać różne typy, w zależności od parametrów, możesz użyć delegata Func, określającego Typ zwracania.

 11
Author: Oded,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-02 07:57:45

Jest to tylko predefiniowany ogólny delegat. Używając go nie musisz deklarować każdego delegata. Istnieje inny predefiniowany delegat Action<T, T2...>, który jest taki sam, ale zwraca void.

 6
Author: Stefan Steinegger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-02 08:02:47

Uważam, że Func<T> jest bardzo przydatny, gdy tworzę komponent, który musi być spersonalizowany "w locie".

Weźmy ten bardzo prosty przykład: składnik PrintListToConsole<T>.

Bardzo prosty obiekt, który wyświetla tę listę obiektów w konsoli. Chcesz, aby programista, który go używa, spersonalizował wynik.

Na przykład, chcesz pozwolić mu zdefiniować konkretny typ formatu liczb i tak dalej.

Bez Func

Najpierw musisz stworzyć interfejs dla klasy, która pobiera dane wejściowe i tworzy ciąg znaków do wydrukowania na konsoli.

interface PrintListConsoleRender<T> {
  String Render(T input);
}

Następnie musisz utworzyć klasę PrintListToConsole<T>, która pobiera wcześniej utworzony interfejs i używa go nad każdym elementem listy.

class PrintListToConsole<T> {

    private PrintListConsoleRender<T> _renderer;

    public void SetRenderer(PrintListConsoleRender<T> r) {
        // this is the point where I can personalize the render mechanism
        _renderer = r;
    }

    public void PrintToConsole(List<T> list) {
        foreach (var item in list) {
            Console.Write(_renderer.Render(item));
        }
    }   
}

Programista, który musi użyć Twojego komponentu musi:

  1. Implementacja interfejsu

  2. Przekazać prawdziwą klasę PrintListToConsole

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

Korzystanie z Func to dużo prostsze

Wewnątrz komponentu definiujesz parametr typu Func<T,String>, który reprezentuje interfejs funkcji , która pobiera parametr wejściowy typu T i zwraca łańcuch (wyjście dla konsoli)

class PrintListToConsole<T> {

    private Func<T, String> _renderFunc;

    public void SetRenderFunc(Func<T, String> r) {
        // this is the point where I can set the render mechanism
        _renderFunc = r;
    }

    public void Print(List<T> list) {
        foreach (var item in list) {
            Console.Write(_renderFunc(item));
        }
    }
}

Kiedy programista używa Twojego komponentu, po prostu przekazuje do niego implementację typu Func<T, String>, czyli funkcję, która tworzy wyjście dla konsoli.

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

Func<T> pozwala zdefiniować ogólny interfejs metody w locie. Definiujesz, jakiego typu jest wejście i jaki jest typ wyjścia. Proste i zwięzłe.

 6
Author: Marco Staffoli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-25 09:49:37