Jak tworzyć funkcje inline w C #


103

Używam Linq To XML

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

ale chcę wykonać obliczenia do przedrostka przed dodaniem go do xml, na przykład wyeliminowanie spacji, znaków specjalnych, niektórych obliczeń itp.

Nie chcę tworzyć funkcji, ponieważ nie będą one pomocne w żadnej innej części mojego programu, ale to, więc czy istnieje sposób na tworzenie funkcji wbudowanych?


5
Nie zabraniaj sobie tworzenia funkcji tylko dlatego, że nie będzie wywoływana z żadnego innego miejsca. Funkcje mogą być również używane do czyszczenia kodu (jeśli masz dużą procedurę obejmującą kilka stron, dzieląc go na funkcje, co czyni go bardziej czytelnym) i dokumentem własnym (samo nazwanie funkcji może przekazać informacje czytelnikowi).
Joe

14
Jeśli przyszedłeś tu z Google i chcesz wiedzieć, jak emulować funkcje wbudowane w języku C #, przejdź tutaj: stackoverflow.com/questions/473782/inline-functions-in-c Poniższe odpowiedzi nie dotyczą funkcji „inline” w prawdziwym znaczeniu tego słowa, a c # nie ma nawet prawdziwych funkcji wbudowanych, ale powyższy link zapewnia optymalizację kompilatora, której można użyć.
JamesHoux

Dzięki James, tytuł pytania jest rzeczywiście raczej mylący.
Zar Shardan

Odpowiedzi:


237

Tak, C # to obsługuje. Dostępnych jest kilka składni.

  • Anonimowe metody zostały dodane w 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!");
    }
    
  • Lambdy są nowością w C # 3.0 i występują w dwóch smakach.

    • Lambdy wyrażeń:

      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
      
    • Lambdy instrukcji:

      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; };
      
  • Funkcje lokalne zostały wprowadzone w C # 7.0:

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

Istnieją zasadniczo dwa różne ich typy: Funci Action. Funcs zwracają wartości, ale Actionnie. Ostatnim parametrem typu a Funcjest typ zwracany; wszystkie inne są typami parametrów.

Istnieją podobne typy z różnymi nazwami, ale składnia deklarowania ich w tekście jest taka sama. Przykładem tego jest Comparison<T>, co z grubsza odpowiada 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

Można je wywołać bezpośrednio, tak jakby były zwykłymi metodami:

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
Nie rozumiem tej odpowiedzi. W swoim przykładzie chce, aby nowy XElement ("Prefix", prefix => newPrefix) zadeklarowany w tekście. Wszystkie te odpowiedzi sprowadzają się do deklaracji nowej funkcji. Dlaczego po prostu nie zadeklarować nowej funkcji?
Matthew James Davis

3
ponieważ: 1. zrozumienie logiki czytania metody w funkcji inline jest znacznie łatwiejsze niż wyszukiwanie różnych deklaracji 2. często trzeba pisać mniej kodu używając funkcji inline
Volodymyr

37

C # 7 dodaje obsługę funkcji lokalnych

Oto poprzedni przykład z użyciem funkcji lokalnej

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

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

30

Odpowiedź na Twoje pytanie brzmi tak i nie, w zależności od tego, co rozumiesz przez „funkcję inline”. Jeśli używasz terminu tak, jak jest on używany w programowaniu w C ++, odpowiedź brzmi: nie, nie możesz tego zrobić - nawet wyrażenie lambda jest wywołaniem funkcji. Chociaż prawdą jest, że można zdefiniować wbudowane wyrażenia lambda, aby zastąpić deklaracje funkcji w C #, kompilator nadal tworzy anonimową funkcję.

Oto naprawdę prosty kod, którego użyłem do przetestowania tego (VS2015):

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

Co generuje kompilator? Użyłem sprytnego narzędzia o nazwie ILSpy, które pokazuje rzeczywisty wygenerowany zestaw IL. Spójrz (pominąłem wiele rzeczy związanych z konfiguracją klas)

To jest główna funkcja:

        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

Widzisz te linie IL_0026 i IL_0027? Te dwie instrukcje ładują liczbę 5 i wywołują funkcję. Następnie sformatuj IL_0031 i IL_0036 i wydrukuj wynik.

A oto funkcja o nazwie:

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

To naprawdę krótka funkcja, ale jest to funkcja.

Czy warto to zoptymalizować? Nie. Może, jeśli wywołujesz to tysiące razy na sekundę, ale jeśli wydajność jest tak ważna, powinieneś rozważyć wywołanie kodu natywnego napisanego w C / C ++, aby wykonać tę pracę.

Z mojego doświadczenia wynika, że ​​czytelność i łatwość konserwacji są prawie zawsze ważniejsze niż optymalizacja na kilka mikrosekund przyspieszenia. Użyj funkcji, aby uczynić swój kod czytelnym i kontrolować zakres zmiennych i nie martw się o wydajność.

„Przedwczesna optymalizacja jest źródłem wszelkiego zła (a przynajmniej większości) w programowaniu”. - Donald Knuth

„Program, który nie działa poprawnie, nie musi działać szybko” - Ja


4
To największy hit Google w zakresie wbudowanych funkcji C #. Mówisz „Może, jeśli wywołujesz to tysiące razy na sekundę” i zgadnij co - ja nazywam to 4 miliony razy na sekundę, więc tak ... wstawianie w tekście prawdopodobnie przydałoby się (i znacznie zwiększyłoby czytelność kodu w porównaniu z czym Muszę zrobić: ręczne rozwinięcie jednej funkcji w 16 miejscach).
Adam

Dla osób zaznajomionych z Kotlinem oznacza to, że inlinefunkcje Kotlina nie mają odpowiednika w C #.
Vapid Linus

17

Tak.

Możesz tworzyć anonimowe metody lub wyrażenia lambda :

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

Dziękuję za odpowiedź, czy mógłbyś rozwinąć nieco więcej odpowiedzi, lamba, mam na myśli codeFunc <string, string> PrefixTrimmer = x => x ?? ""; codeJestem nowy w tym i naprawdę nie rozumiem zbyt dobrze dokumentu MSDN
Joe Dixon

2
@Joe: x ?? ""to operator łączący wartość null. msdn.microsoft.com/en-us/library/ms173224.aspx
SLaks

W x => (something), xjest parametrem i (something)jest wartością zwracaną`.
SLaks


2
@Hellfrost: Przeczytaj pytanie. Nie pyta o funkcje wbudowane w stylu C ++. Nie chcę tworzyć funkcji, ponieważ te nie będą pomocne w żadnej innej części mojego programu, ale to
SLaks

5

Możesz użyć Func, który hermetyzuje metodę, która ma jeden parametr i zwraca wartość typu określonego przez parametr TResult.

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


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

4

Nie tylko metody Inside, mogą być również używane wewnątrz klas.

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);
    }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.