web-dev-qa-db-fra.com

Qu'est-ce que Func, comment et quand est-il utilisé

Quel est Func<> et à quoi sert-il?

104
learning

Func<T> est un type de délégué prédéfini pour une méthode qui renvoie une valeur du type T.

En d'autres termes, vous pouvez utiliser ce type pour référencer une méthode qui renvoie une valeur de T. Par exemple.

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

peut être référencé comme ça

Func<string> f = GetMessage;
70
Brian Rasmussen

Pensez-y comme un espace réservé. Cela peut être très utile lorsque le code suit un certain modèle mais n’est pas nécessairement lié à une fonctionnalité particulière.

Par exemple, considérons le Enumerable.Select méthode d'extension.

  • Le motif est le suivant: pour chaque élément d'une séquence, sélectionnez une valeur à partir de cet élément (une propriété, par exemple) et créez une nouvelle séquence composée de ces valeurs.
  • Le espace réservé est: une fonction de sélection qui obtient les valeurs de la séquence décrite ci-dessus.

Cette méthode prend un Func<T, TResult> au lieu de toute fonction concrète. Cela lui permet d'être utilisé dans un contexte quelconque où le modèle ci-dessus s'applique.

Donc, par exemple, disons que j'ai un List<Person> et je veux juste le nom de chaque personne de la liste. Je peux le faire:

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

Ou dire que je veux le age de chaque personne:

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

Tout de suite, vous pouvez voir comment j'ai pu exploiter le code identique représentant un motif (avec Select) avec deux différent = fonctions (p => p.Name et p => p.Age).

L'alternative serait d'écrire une version différente de Select à chaque fois que vous souhaitiez analyser une séquence avec un type de valeur différent. Donc, pour obtenir le même effet que ci-dessus, il me faudrait:

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

Avec un délégué agissant en tant que remplaçant, je me libère de l'obligation d'écrire le même modèle à plusieurs reprises dans des cas comme celui-ci.

80
Dan Tao

Func<T1, T2, ..., Tn, Tr> représente une fonction qui prend (T1, T2, ..., Tn) des arguments et retourne Tr.

Par exemple, si vous avez une fonction:

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

Vous pouvez l'enregistrer sous une sorte de variable de fonction:

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

Et utilisez ensuite exactement comme vous utiliseriez sqr:

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

etc.

Rappelez-vous cependant que c'est un délégué, pour plus d'informations, reportez-vous à la documentation.

61
Grozz

Func<T1,R> et les autres Func délégués génériques prédéfinis (Func<T1,T2,R>, Func<T1,T2,T3,R> et autres) sont des délégués génériques qui renvoient le type du dernier paramètre générique.

Si vous avez une fonction qui doit renvoyer différents types, en fonction des paramètres, vous pouvez utiliser un délégué Func, en spécifiant le type de retour.

11
Oded

Je trouve Func<T> très utile lorsque je crée un composant qui doit être personnalisé "à la volée".

Prenons cet exemple très simple: un PrintListToConsole<T> _ composant.

Un objet très simple qui imprime cette liste d'objets sur la console. Vous souhaitez laisser le développeur qui l'utilise personnaliser la sortie.

Par exemple, vous voulez lui laisser définir un type particulier de format numérique, etc.

sans fonction

Tout d'abord, vous devez créer une interface pour une classe qui prend l'entrée et génère la chaîne à imprimer sur la console.

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

Ensuite, vous devez créer la classe PrintListToConsole<T> qui reprend l’interface créée précédemment et l’utilise sur chaque élément de la liste.

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

Le développeur qui doit utiliser votre composant doit:

  1. implémenter l'interface

  2. passer la vraie classe au 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();   
        }   
    }
    

tiliser Func c'est beaucoup plus simple

Dans le composant, vous définissez un paramètre de type Func<T,String> que représente l'interface d'une fonction qui prend un paramètre d'entrée de type T et retourne une chaîne (le résultat de la console)

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

Lorsque le développeur utilise votre composant, il lui transmet simplement la mise en oeuvre de la Func<T, String> type, c'est une fonction qui crée la sortie pour la console.

class Program {
    static void Main(string[] args) {
        var list = new List<int> { 1, 2, 3 }; // should be a list as the method signature expects
        var printer = new PrintListToConsole<int>();
        printer.SetRenderFunc((o) => "Number:" + o);
        printer.Print(list); 
        string result = Console.ReadLine();
    }
}

Func<T> vous permet de définir une interface de méthode générique à la volée. Vous définissez le type d'entrée et le type de sortie. Simple et concis.

11
Marco Staffoli

C'est juste un délégué générique prédéfini. En l'utilisant, vous n'avez pas besoin de déclarer chaque délégué. Il y a un autre délégué prédéfini, Action<T, T2...>, qui est identique mais renvoie nul.

6
Stefan Steinegger

Peut-être qu'il n'est pas trop tard pour ajouter des informations.

somme:

Le Func est un délégué personnalisé défini dans l'espace de noms System qui vous permet de pointer vers une méthode avec la même signature (comme le font les délégués), en utilisant 0 à 16 paramètres d'entrée et qui doit renvoyer quelque chose.

Nomenclature & how2use:

Func<input_1, input_2, ..., input1_6, output> funcDelegate = someMethod;

Définition:

public delegate TResult Func<in T, out TResult>(T arg);

Où il est utilisé:

Il est utilisé dans les expressions lambda et les méthodes anonymes.

0
overRideKode