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 :
-
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.
-
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.
-
En règle générale, si la fonction fait plus d'une ligne, je préfère une méthode.
-
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);
}
-
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.