39 votes

Action/Func vs Méthodes, quel est l'intérêt ?

Je sais comment utiliser Action y Func dans .NET, mais à chaque fois que je commence à le faire, la même solution peut être obtenue avec une bonne vieille méthode que j'appelle à la place.

Cela exclut le cas où un Action ou Func est utilisé comme argument pour quelque chose que je ne contrôle pas, comme la fonction LINQ .Where .

Donc, en gros, ma question est... pourquoi existent-ils ? Qu'est-ce qu'elles m'apportent de plus et de nouveau qu'une simple Méthode ?

41voto

nawfal Points 13500

Je pense que d'autres réponses ici parlent de ce qu'un Action / Func est et son utilisation. Je vais essayer de répondre à la question suivante : comment choisir entre Action / Func et la méthode. Les différences d'abord :

1) Du point de vue de la performance brute, les délégués sont plus lents que les appels directs de méthodes mais c'est tellement insignifiant que s'en préoccuper est une mauvaise pratique.

2) Les méthodes peuvent avoir des surcharges (mêmes noms de fonctions avec des signatures différentes) mais pas les Action / Func puisqu'ils sont déclarés comme des variables et que, selon les règles de C#, vous ne pouvez pas avoir deux variables avec le même nom dans une portée donnée.

bool IsIt() { return 1 > 2; }
bool IsIt(int i) { return i > 2; } //legal

Func<bool> IsIt = () => 1 > 2; 
Func<int, bool> IsIt = i => i > 2; //illegal, duplicate variable naming

3) En conséquence, Action / Func sont réaffectables et peuvent pointer vers n'importe quelle fonction, tandis que les méthodes, une fois compilées, restent les mêmes pour toujours. Il est sémantiquement incorrect d'utiliser Func/Action si la méthode vers laquelle il pointe ne change jamais pendant l'exécution.

bool IsIt() { return 1 > 2; } //always returns false

Func<bool> IsIt = () => 1 > 2; 
IsIt = () => 2 > 1; //output of IsIt depends on the function it points to.

4) Vous pouvez spécifier ref / out paramètres pour les méthodes normales. Par exemple, vous pouvez avoir

bool IsIt(out string p1, ref int p2) { return 1 > 2; } //legal

Func<out string, ref int, bool> IsIt; //illegal

5) Vous ne pouvez pas introduire de nouveau paramètre de type générique pour Action / Func (ils sont déjà génériques, mais les arguments de type ne peuvent être qu'un type connu ou des types spécifiés dans la méthode ou la classe parente), contrairement aux méthodes.

bool IsIt<A, R>() { return 1 > 2; } //legal

Func<bool> IsIt<A, R> = () => 1 > 2; //illegal

6) Les méthodes peuvent avoir des paramètres facultatifs, pas Action / Func .

bool IsIt(string p1 = "xyz") { return 1 > 2; } //legal

Func<string, bool> IsIt = (p1 = "xyz") => 1 > 2; //illegal

7) Vous pouvez avoir params pour les paramètres d'une méthode, ce qui n'est pas le cas avec le mot-clé Action / Func .

bool IsIt(params string[] p1) { return 1 > 2; } //legal

Func<params string[], bool> IsIt = p1 => 1 > 2; //illegal

8) Intellisense joue bien avec les noms de paramètres des méthodes (et par conséquent vous avez une documentation XML sympa disponible pour les méthodes), ce qui n'est pas le cas de Action / Func . Ainsi, en ce qui concerne la lisibilité, les méthodes régulières sont gagnantes.

9) Action / Func ont une limite de paramètres de 16 (non pas que vous ne puissiez pas définir vos propres paramètres avec plus) mais les méthodes supportent plus que vous n'en aurez jamais besoin.

Pour ce qui est de savoir quand utiliser lequel, je prendrais en compte les éléments suivants :

  1. Lorsque vous êtes contraint d'en utiliser un sur la base de l'un des points ci-dessus, vous n'avez de toute façon pas d'autre choix. Le point 3 est le plus convaincant, à mon avis, sur lequel vous devrez fonder votre décision.

  2. Dans la plupart des cas normaux, une méthode régulière est la voie à suivre. C'est la manière standard de remanier un ensemble de fonctionnalités communes dans le monde C# et VB.NET.

  3. En règle générale, si la fonction fait plus d'une ligne, je préfère une méthode.

  4. Si la fonction n'a aucune pertinence en dehors d'une méthode spécifique et que la fonction est trop triviale, comme un simple sélecteur ( Func<S, T> ) ou un prédicat ( Func<bool> ) Je préférerais Action / Func . Par exemple,

    public static string GetTimeStamp() 
    {
        Func<DateTime, string> f = dt => humanReadable 
                                       ? dt.ToShortTimeString() 
                                       : dt.ToLongTimeString();
        return f(DateTime.Now);
    }
  5. Il pourrait y avoir des situations où Action / Func est plus logique. Par exemple, si vous devez construire une expression lourde et compiler un délégué, cela vaut la peine de le faire une seule fois et de mettre en cache le délégué compilé.

    public static class Cache<T> 
    { 
        public static readonly Func<T> Get = GetImpl();
    
        static Func<T> GetImpl()
        {
            //some expensive operation here, and return a compiled delegate
        }
    }

    au lieu de

    public static class Cache<T> 
    {
        public static T Get()
        {
            //build expression, compile delegate and invoke the delegate
        }
    }

    Dans le premier cas, lorsque vous appelez Get , GetImpl n'est exécuté qu'une seule fois, alors que dans le second cas, (coûteux) Get sera appelé à chaque fois.


Sans oublier que la méthode anonyme elle-même aura certaines limites sans rapport avec Func/Action ce qui rend l'utilisation un peu différente. Voir aussi ceci pour une question connexe.

23voto

Chris Shain Points 33569

Action et Func sont fournis par le cadre Délégué types. Les délégués permettent aux fonctions d'être traitées comme des variables, ce qui signifie que vous pouvez (entre autres) les transmettre de méthode en méthode. Si vous avez déjà programmé en C++, vous pouvez considérer les délégués comme des pointeurs de fonction qui sont limités par la signature de la méthode à laquelle ils font référence.

Action et Func sont spécifiquement des délégués génériques (ce qui signifie qu'ils prennent des paramètres de type) avec certaines des signatures les plus courantes - presque toutes les méthodes dans la plupart des programmes peuvent être représentées en utilisant l'une ou l'autre de ces deux méthodes, ce qui permet aux gens de gagner beaucoup de temps en définissant manuellement les délégués comme nous le faisions dans .net avant la version 2. En fait, lorsque je vois du code comme celui-ci dans un projet, je peux généralement supposer sans risque que le projet a été migré de .net 1.1 :

// This defines a delegate (a type that represents a function)
// but usages could easily be replaced with System.Action<String>
delegate void SomeApplicationSpecificName(String someArgument);

Je vous recommande d'examiner les délégués de plus près. Il s'agit d'une fonctionnalité extrêmement puissante du langage C#.

7voto

Hand-E-Food Points 5510

Je les utilise pour créer un tableau de fonctions. Par exemple, je peux avoir une ComboBox remplie d'actions qui pourraient être prises. Je remplis la ComboBox avec des éléments d'une classe ou d'une structure :

public class ComboBoxAction
{
    private string text;
    private Action method;

    public ComboBoxAction(string text, Action method)
    {
        this.text = text;
        this.method = method;
    }

    public override string ToString()
    {
        return this.text;
    }

    public void Go()
    {
        this.method();
    }
}

Ensuite, quand quelqu'un sélectionne un élément, je peux appeler l'action.

CType(ComboBox1.SelectedItem, ComboBoxAction).Go()

C'est beaucoup plus simple que de demander à une instruction Select de déterminer la méthode à appeler en fonction du texte de la ComboBox.

3voto

Kirk Broadhurst Points 13093

Il y a beaucoup de cas où un Func peut aider là où une Méthode ne le ferait pas.

public void DoThing(MyClass foo, Func<MyClass, string> func)
{
    foo.DoSomething;
    var result = func(foo);
    foo.DoStringThing(result);
}

Vous pouvez donc spécifier un Func différent à chaque fois que vous appelez cette méthode - l'option DoThing n'a pas besoin de savoir ce qui est fait, il suffit que ce qui est fait renvoie une chaîne.

Vous pouvez le faire sans utiliser le mot-clé Func en utilisant la fonction delegate à la place ; cela fonctionne à peu près de la même manière.

2voto

Lijo Points 4002

Une grande utilisation de action y func sont les cas où nous devons effectuer une opération (avant ou après une méthode), indépendamment de la méthode en question. Par exemple, nous devons réessayer la méthode 10 fois si une exception se produit.

Considérons la méthode suivante - son type de retour est generic . Il peut donc être appliqué sur func avec n'importe quel type de retour.

public static T ExecuteMultipleAttempts<T>(Func<T> inputMethod, Action additionalTask, int wait, int numOfTimes)
        {
            var funcResult = default(T);
            int counter = 0;
            while (counter < numOfTimes)
            {
                try
                {
                    counter++;
                    funcResult = inputMethod();

                    //If no exception so far, the next line will break the loop.
                    break;
                }
                catch (Exception ex)
                {
                    if (counter >= numOfTimes)
                    {
                        //If already exceeded the number of attemps, throw exception
                        throw;
                    }
                    else
                    {
                        Thread.Sleep(wait);
                    }

                    if (additionalTask != null)
                    {
                        additionalTask();
                    }
                }
            }

            return funcResult;
        }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X