Come creare funzioni inline in C #


98

Sto usando Linq To XML

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

ma voglio fare dei calcoli sul prefisso prima di aggiungerlo all'xml, come eliminare spazi, caratteri speciali, alcuni calcoli ecc.

Non voglio creare funzioni perché queste non saranno di alcun aiuto in nessun'altra parte del mio programma ma questo, quindi c'è un modo per creare funzioni inline ??


5
Non proibirti di creare una funzione solo perché non verrà chiamata da nessun altro posto. Le funzioni possono anche essere utilizzate per ripulire il codice (se si dispone di una procedura di grandi dimensioni che si estende su diverse pagine, suddividerla in funzioni lo rende più leggibile) e auto documento (solo nominare una funzione correttamente può trasmettere informazioni al lettore.)
Joe

14
Se sei arrivato qui da Google e vuoi sapere come emulare le funzioni inline in c #, vai qui: stackoverflow.com/questions/473782/inline-functions-in-c Le risposte seguenti non riguardano le funzioni "inline" nel vero senso della parola, ec # non ha nemmeno vere funzioni inline, ma il collegamento sopra fornisce un'ottimizzazione del compilatore che può essere utilizzata.
JamesHoux

Grazie James, il titolo della domanda è davvero fuorviante.
Zar Shardan

Risposte:


227

Sì, C # lo supporta. Sono disponibili diverse sintassi.

  • Metodi anonimi sono stati aggiunti in 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!");
    }
    
  • Le lambda sono nuove in C # 3.0 e sono disponibili in due versioni.

    • Espressione lambda:

      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
      
    • Frasi lambda:

      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; };
      
  • Le funzioni locali sono state introdotte con C # 7.0:

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

Esistono fondamentalmente due tipi diversi per questi: Funce Action. Funcs restituiscono valori ma Actions no. L'ultimo parametro di tipo di a Funcè il tipo restituito; tutti gli altri sono i tipi di parametro.

Esistono tipi simili con nomi diversi, ma la sintassi per dichiararli inline è la stessa. Un esempio di ciò è Comparison<T>, che è più o meno equivalente a 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

Questi possono essere richiamati direttamente come se fossero metodi normali:

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.

4
Non capisco questa risposta. Nel suo esempio, vuole che new XElement ("Prefix", prefix => newPrefix) dichiarato inline. Tutte queste risposte equivalgono a dichiarare una nuova funzione. Perché non dichiarare semplicemente una nuova funzione?
Matthew James Davis

3
perché: 1. comprendere la logica del metodo di lettura della funzione inline è molto più semplice che cercare diverse dichiarazioni 2. spesso è necessario scrivere meno codice quando si utilizzano le funzioni inline
Volodymyr

35

C # 7 aggiunge il supporto per le funzioni locali

Ecco l'esempio precedente che utilizza una funzione locale

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

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

29

La risposta alla tua domanda è sì e no, a seconda di cosa intendi per "funzione inline". Se stai usando il termine come è usato nello sviluppo C ++, la risposta è no, non puoi farlo, anche un'espressione lambda è una chiamata di funzione. Sebbene sia vero che puoi definire espressioni lambda inline per sostituire le dichiarazioni di funzione in C #, il compilatore finisce comunque per creare una funzione anonima.

Ecco un codice davvero semplice che ho usato per testarlo (VS2015):

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

Cosa genera il compilatore? Ho usato uno strumento elegante chiamato ILSpy che mostra l'assembly IL reale generato. Dai un'occhiata (ho omesso molte cose sull'impostazione della classe)

Questa è la funzione 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

Vedi quelle righe IL_0026 e IL_0027? Queste due istruzioni caricano il numero 5 e richiamano una funzione. Quindi IL_0031 e IL_0036 formattano e stampano il risultato.

Ed ecco la funzione chiamata:

        .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'

È una funzione davvero breve, ma è una funzione.

Vale la pena fare uno sforzo per ottimizzarlo? Nah. Forse se lo chiami migliaia di volte al secondo, ma se le prestazioni sono così importanti, dovresti considerare di chiamare il codice nativo scritto in C / C ++ per fare il lavoro.

Nella mia esperienza la leggibilità e la manutenibilità sono quasi sempre più importanti dell'ottimizzazione per un guadagno di velocità di pochi microsecondi. Usa le funzioni per rendere leggibile il tuo codice e per controllare l'ambito delle variabili senza preoccuparti delle prestazioni.

"L'ottimizzazione prematura è la radice di tutti i mali (o almeno della maggior parte di essi) nella programmazione". - Donald Knuth

"Un programma che non funziona correttamente non ha bisogno di essere eseguito velocemente" - Me


3
Questo è uno dei migliori risultati di Google per le funzioni C # incorporate. Dici "Forse se lo chiami migliaia di volte al secondo" e indovina un po '- lo chiamo 4 milioni di volte al secondo, quindi sì ... l'inlining sarebbe probabilmente utile (e aumenterebbe enormemente la leggibilità del codice rispetto a quanto Sto per fare: srotolamento manuale di una funzione in 16 posti).
Adam

17

Sì.

Puoi creare metodi anonimi o espressioni lambda :

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

Grazie per la tua risposta, potresti approfondire un po 'di più sulla risposta, quella lamba, intendo il codeFunc <string, string> PrefixTrimmer = x => x ?? ""; codeSono nuovo a questo e davvero non capisco molto bene il documento MSDN
Joe Dixon

2
@ Joe: x ?? ""è l'operatore di coalescenza null. msdn.microsoft.com/en-us/library/ms173224.aspx
SLaks

In x => (something), xè un parametro ed (something)è il valore restituito`.
SLaks


2
@ Hellfrost: leggi la domanda. Non sta chiedendo delle funzioni inline in stile C ++. Non voglio creare funzioni perché queste non saranno di alcun aiuto in nessun'altra parte del mio programma tranne questa
SLaks

5

È possibile utilizzare Func che incapsula un metodo che ha un parametro e restituisce un valore del tipo specificato dal parametro TResult.

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


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

4

Non solo i metodi Inside, possono essere utilizzati anche all'interno delle classi.

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);
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.