web-dev-qa-db-fra.com

Comment créer des fonctions inline en C #

J'utilise Linq To XML

new XElement("Prefix", Prefix == null ? "" : Prefix)

mais je veux faire quelques calculs au préfixe avant de l'ajouter au xml, comme éliminer les espaces, les caractères spéciaux, certains calculs, etc. 

Je ne veux pas créer de fonctions car celles-ci ne seront d'aucune utilité pour aucune autre partie de mon programme, mais est-ce qu'il y a un moyen de créer des fonctions inline?

67
Joe Dixon

Oui, C # soutient cela. Il existe plusieurs syntaxes disponibles.

  • Les méthodes anonymes ont été ajoutées en C # 2.0:

    Func<int, int, int> add = delegate(int x, int y)
    {
        return x + y;
    };
    Action<int> print = delegate(int x)
    {
        Console.WriteLine(x);
    }
    Action<int> helloWorld = delegate // parameters can be elided if ignored
    {
        Console.WriteLine("Hello world!");
    }
    
  • Lambdas sont nouveaux dans C # 3.0 et sont proposés en deux versions.

    • Lambdas d'expression:

      Func<int, int, int> add = (int x, int y) => x + y; // or...
      Func<int, int, int> add = (x, y) => x + y; // types are inferred by the compiler
      
    • Déclaration lambdas:

      Action<int> print = (int x) => { Console.WriteLine(x); };
      Action<int> print = x => { Console.WriteLine(x); }; // inferred types
      Func<int, int, int> add = (x, y) => { return x + y; };
      
  • Les fonctions locales ont été introduites avec C # 7.0:

    int add(int x, int y) => x + y;
    void print(int x) { Console.WriteLine(x); }
    

Il existe fondamentalement deux types différents pour ceux-ci: Func et Action . Funcs retournent des valeurs mais Actions n'en ont pas. Le dernier paramètre de type de Func est le type de retour; tous les autres sont les types de paramètres.

Il existe des types similaires avec des noms différents, mais la syntaxe pour les déclarer en ligne est la même. Un exemple de ceci est Comparison<T> , ce qui est à peu près équivalent à Func<T, T, int>.

Func<string, string, int> compare1 = (l,r) => 1;
Comparison<string> compare2 = (l, r) => 1;
Comparison<string> compare3 = compare1; // this one only works from C# 4.0 onwards

Celles-ci peuvent être invoquées directement comme s'il s'agissait de méthodes régulières:

int x = add(23, 17); // x == 40
print(x); // outputs 40
helloWorld(x); // helloWorld has one int parameter declared: Action<int>
               // even though it does not make any use of it.
165

La réponse à votre question est oui et non, selon ce que vous entendez par "fonction inline". Si vous utilisez le terme tel qu'il est utilisé dans le développement C++, la réponse est non, vous ne pouvez pas le faire - même une expression lambda est un appel de fonction. S'il est vrai que vous pouvez définir des expressions lambda inline pour remplacer les déclarations de fonction en C #, le compilateur finit toujours par créer une fonction anonyme.

Voici un code très simple que j'ai utilisé pour tester ceci (VS2015):

    static void Main(string[] args)
    {
        Func<int, int> incr = a => a + 1;
        Console.WriteLine($"P1 = {incr(5)}");
    }

Que génère le compilateur? J'ai utilisé un outil astucieux appelé ILSpy qui montre le véritable assemblage IL généré. Jetez un coup d'oeil (j'ai omis beaucoup de choses d'installation de classe)

C'est la fonction principale:

        IL_001f: stloc.0
        IL_0020: ldstr "P1 = {0}"
        IL_0025: ldloc.0
        IL_0026: ldc.i4.5
        IL_0027: callvirt instance !1 class [mscorlib]System.Func`2<int32, int32>::Invoke(!0)
        IL_002c: box [mscorlib]System.Int32
        IL_0031: call string [mscorlib]System.String::Format(string, object)
        IL_0036: call void [mscorlib]System.Console::WriteLine(string)
        IL_003b: ret

Voir les lignes IL_0026 et IL_0027? Ces deux instructions chargent le numéro 5 et appellent une fonction. Ensuite, IL_0031 et IL_0036 formatez et imprimez le résultat.

Et voici la fonction appelée:

        .method Assembly hidebysig 
            instance int32 '<Main>b__0_0' (
                int32 a
            ) cil managed 
        {
            // Method begins at RVA 0x20ac
            // Code size 4 (0x4)
            .maxstack 8

            IL_0000: ldarg.1
            IL_0001: ldc.i4.1
            IL_0002: add
            IL_0003: ret
        } // end of method '<>c'::'<Main>b__0_0'

C'est une fonction très courte, mais c'est une fonction.

Cela vaut-il la peine d’être optimisé? Nah. Peut-être que si vous l'appelez des milliers de fois par seconde, mais si les performances sont si importantes, vous devriez envisager d'appeler du code natif en C/C++ pour effectuer le travail. 

D'après mon expérience, la lisibilité et la maintenabilité sont presque toujours plus importantes que l'optimisation d'un gain de vitesse de quelques microsecondes. Utilisez des fonctions pour rendre votre code lisible et pour contrôler la portée des variables sans vous soucier des performances.

"L'optimisation prématurée est la racine de tout le mal (ou du moins de la majeure partie de celui-ci) dans la programmation." -- Donald Knuth

"Un programme qui ne fonctionne pas correctement n'a pas besoin de courir vite" -- Moi

17
Ray Fischer

Oui.

Vous pouvez créer méthodes anonymes ou expressions lambda :

Func<string, string> PrefixTrimmer = delegate(string x) {
    return x ?? "";
};
Func<string, string> PrefixTrimmer = x => x ?? "";
16
SLaks

C # 7 ajoute le support pour les fonctions locales

Voici l'exemple précédent utilisant une fonction locale

void Method()
{
    string localFunction(string source)
    {
        // add your functionality here
        return source ;
    };

   // call the inline function
   localFunction("prefix");
}
16
DalSoft

Vous pouvez utiliser Func qui encapsule une méthode comportant un paramètre et renvoyant une valeur du type spécifié par le paramètre TResult.

void Method()
{
    Func<string,string> inlineFunction = source => 
    {
        // add your functionality here
        return source ;
     };


    // call the inline function
   inlineFunction("prefix");
}
5
Homam

Non seulement les méthodes Inside, elles peuvent également être utilisées dans des classes.

class Calculator
    {
        public static int Sum(int x,int y) => x + y;
        public static Func<int, int, int>  Add = (x, y) => x + y;
        public static Action<int,int> DisplaySum = (x, y) => Console.WriteLine(x + y);
    }
0
Deepak Mishra