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

115

Qu'est- Func<>ce que c'est et à quoi sert-il?

apprentissage
la source
4
C'est juste un raccourci pour les délégués avec une signature spécifique. Pour bien comprendre les réponses ci-dessous, vous devrez comprendre les délégués ;-)
Theo Lenndorff
2
Dans la réponse de @Oded, il ditIf you have a function that needs to return different types, depending on the parameters, you can use a Func delegate, specifying the return type.
LCJ

Réponses:

76

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 ceci

Func<string> f = GetMessage;
Brian Rasmussen
la source
Mais cela peut aussi représenter une fonction statique à un argument =)
Ark-kun
2
@ Ark-kun non, ce n'est pas correct. La définition de Func<T>est delegate TResult Func<out TResult>(). Aucun argument. Func<T1, T2>serait une fonction qui prend un argument.
Brian Rasmussen
4
Non, j'ai raison. static int OneArgFunc(this string i) { return 42; } Func<int> f = "foo".OneArgFunc;. =)
Ark-kun
1
C'est une méthode d'extension qui est spéciale.
Brian Rasmussen
La seule particularité à ce sujet est l' Extensionattribut qui n'est lu que par les compilateurs C # / VB.Net, pas par CLR. Fondamentalement, les méthodes d'instance (contrairement aux fonctions statiques) ont un 0ème paramètre caché "this". Ainsi, la méthode d'instance à 1 argument est très similaire à la fonction statique à 2 arguments. Ensuite, nous avons des délégués qui stockent l' objet cible et le pointeur de fonction . Les délégués peuvent stocker le premier argument dans target ou ne pas le faire.
Ark-kun
87

Considérez-le comme un espace réservé. Cela peut être très utile lorsque vous avez du code qui suit un certain modèle mais qui n'a pas besoin d'être lié à une fonctionnalité particulière.

Par exemple, considérez la Enumerable.Selectméthode d'extension.

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

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

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 dis que je veux l' âge de chaque personne:

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

Tout de suite, vous pouvez voir comment j'ai pu exploiter le même code représentant un motif (avec Select) avec deux fonctions différentes ( p => p.Nameet p => p.Age).

L'alternative serait d'écrire une version différente de Selectchaque fois que vous souhaitez analyser une séquence pour 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 comme espace réservé, je me libère de devoir écrire le même modèle encore et encore dans des cas comme celui-ci.

Dan Tao
la source
66

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

Par exemple, si vous avez une fonction:

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

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

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

Et puis utilisez exactement comme vous utiliseriez sqr:

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

etc.

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

Grozz
la source
1
Excellente réponse, mais pour la compilation du mot-clé statique, il est nécessaire
boctulus
16

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 voulez laisser le développeur qui l'utilise personnaliser la sortie.

Par exemple, vous voulez lui laisser définir un type particulier de format de nombre et ainsi de suite.

Sans Func

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

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

Ensuite, vous devez créer la classe PrintListToConsole<T>qui prend l'interface précédemment créée 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 a besoin d'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();   
        }   
    }
    

Utiliser Func c'est beaucoup plus simple

À l'intérieur du composant, vous définissez un paramètre de type Func<T,String>qui représente une interface d'une fonction qui prend un paramètre d'entrée de type T et renvoie une chaîne (la sortie pour 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 transmet simplement au composant l'implémentation du Func<T, String>type, c'est-à-dire 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 de l'entrée et le type de la sortie. Simple et concis.

Marco Staffoli
la source
2
Merci d'avoir publié ce Marco. Cela m'a vraiment aidé. J'essaie de comprendre func depuis un moment et je l'utilise également activement dans ma programmation. Cet exemple effacera le chemin. J'ai dû ajouter la méthode StampaFunc car elle était omise dans le code d'origine, ce qui l'empêchait de s'afficher.
Siwoku Adeola
1
Je pense qu'il y a une ligne manquée dans l'exemple Func, où est l'appel pour la fonction d'impression ou StampaFunc?
Bashar Abu Shamaa
11

Func<T1,R>et les autres génériques prédéfinis Funcdélégués ( Func<T1,T2,R>, Func<T1,T2,T3,R>et d' autres) sont 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 Funcdélégué, en spécifiant le type de retour.

Oded
la source
7

Il s'agit simplement d'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 existe un autre délégué prédéfini Action<T, T2...>, qui est le même mais renvoie void.

Stefan Steinegger
la source
0

Il n'est peut-être 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 et mode d'emploi:

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.

overRideKode
la source