Cara membuat fungsi inline di C #

98

Saya menggunakan Linq To XML

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

tetapi saya ingin melakukan beberapa perhitungan pada awalan sebelum menambahkannya ke xml, seperti menghilangkan spasi, karakter khusus, beberapa perhitungan, dll.

Saya tidak ingin membuat fungsi karena yang ini tidak akan membantu di bagian lain dari program saya tetapi ini, jadi apakah ada cara untuk membuat fungsi sebaris ??

Joe Dixon
sumber
5
Jangan melarang diri Anda membuat fungsi hanya karena fungsi tersebut tidak akan dipanggil dari tempat lain. Fungsi juga dapat digunakan untuk membersihkan kode (jika Anda memiliki prosedur besar yang mencakup beberapa halaman, memecahnya menjadi beberapa fungsi membuatnya lebih mudah dibaca) dan dokumen mandiri (hanya dengan menamai fungsi dengan benar dapat menyampaikan informasi kepada pembaca.)
Joe
14
Jika Anda datang ke sini dari Google dan ingin tahu cara meniru fungsi inline di c #, buka di sini: stackoverflow.com/questions/473782/inline-functions-in-c Jawaban di bawah ini tidak berhubungan dengan fungsi "inline" dalam arti sebenarnya dari kata tersebut, dan c # bahkan tidak memiliki fungsi sebaris yang sebenarnya tetapi tautan di atas menyediakan pengoptimalan kompiler yang dapat digunakan.
JamesHoux
Terima kasih James, judul pertanyaannya memang agak menyesatkan.
Zar Shardan

Jawaban:

228

Ya, C # mendukung itu. Ada beberapa sintaks yang tersedia.

  • Metode anonim ditambahkan di 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!");
    }
    
  • Lambda baru dalam C # 3.0 dan tersedia dalam dua rasa.

    • Ekspresi 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
      
    • Pernyataan 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; };
      
  • Fungsi lokal telah diperkenalkan dengan C # 7.0:

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

Pada dasarnya ada dua jenis yang berbeda untuk ini: Funcdan Action. Funcs mengembalikan nilai tetapi Actions tidak. Parameter tipe terakhir dari a Funcadalah tipe kembali; yang lainnya adalah tipe parameter.

Ada tipe serupa dengan nama berbeda, tetapi sintaksis untuk mendeklarasikannya sebaris adalah sama. Contohnya adalah Comparison<T>, yang secara kasar setara dengan 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

Ini dapat dipanggil secara langsung seolah-olah itu adalah metode biasa:

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.
R. Martinho Fernandes
sumber
4
Saya tidak mengerti jawaban ini. Dalam contoh ini, dia ingin XElement baru ("Prefix", prefix => newPrefix) dideklarasikan sebaris. Semua jawaban ini berarti mendeklarasikan fungsi baru. Mengapa tidak mendeklarasikan fungsi baru saja?
Matthew James Davis
3
karena: 1. untuk memahami logika metode membaca fungsi inline jauh lebih mudah daripada mencari deklarasi yang berbeda 2. sering kali Anda perlu menulis lebih sedikit kode saat menggunakan fungsi inline
Volodymyr
35

C # 7 menambahkan dukungan untuk fungsi lokal

Berikut adalah contoh sebelumnya menggunakan fungsi lokal

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

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

Jawaban atas pertanyaan Anda adalah ya dan tidak, tergantung apa yang Anda maksud dengan "fungsi sebaris". Jika Anda menggunakan istilah seperti yang digunakan dalam pengembangan C ++ maka jawabannya tidak, Anda tidak dapat melakukannya - bahkan ekspresi lambda adalah panggilan fungsi. Meskipun benar bahwa Anda dapat mendefinisikan ekspresi lambda sebaris untuk menggantikan deklarasi fungsi di C #, kompilator masih akan membuat fungsi anonim.

Berikut beberapa kode yang sangat sederhana yang saya gunakan untuk menguji ini (VS2015):

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

Apa yang dihasilkan kompilator? Saya menggunakan alat bagus yang disebut ILSpy yang menunjukkan perakitan IL sebenarnya yang dihasilkan. Coba lihat (Saya telah menghilangkan banyak hal pengaturan kelas)

Ini adalah fungsi utama:

        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

Lihat baris tersebut IL_0026 dan IL_0027? Kedua instruksi tersebut memuat angka 5 dan memanggil suatu fungsi. Kemudian format IL_0031 dan IL_0036 dan print hasilnya.

Dan inilah fungsi yang disebut:

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

Ini adalah fungsi yang sangat singkat, tetapi ini adalah fungsi.

Apakah upaya untuk mengoptimalkan ini sepadan? Nah. Mungkin jika Anda memanggilnya ribuan kali per detik, tetapi jika kinerjanya begitu penting maka Anda harus mempertimbangkan untuk memanggil kode asli yang ditulis dalam C / C ++ untuk melakukan pekerjaan itu.

Dalam pengalaman saya, keterbacaan dan pemeliharaan hampir selalu lebih penting daripada mengoptimalkan untuk mendapatkan kecepatan beberapa mikrodetik. Gunakan fungsi untuk membuat kode Anda dapat dibaca dan untuk mengontrol pelingkupan variabel dan tidak mengkhawatirkan kinerja.

"Pengoptimalan prematur adalah akar dari semua kejahatan (atau setidaknya sebagian besar) dalam pemrograman." - Donald Knuth

"Program yang tidak berjalan dengan benar tidak perlu berjalan cepat" - Saya

Ray Fischer
sumber
3
Ini adalah hit Google teratas untuk fungsi C # sebaris. Anda mengatakan "Mungkin jika Anda meneleponnya ribuan kali per detik", dan coba tebak - saya menyebutnya 4 juta kali per detik, jadi ya ... penyebarisan mungkin akan berguna (dan sangat meningkatkan keterbacaan kode vs apa Saya harus melakukan: membuka gulungan secara manual dari satu fungsi di 16 tempat).
Adam
17

Iya.

Anda dapat membuat metode anonim atau ekspresi lambda :

Func<string, string> PrefixTrimmer = delegate(string x) {
    return x ?? "";
};
Func<string, string> PrefixTrimmer = x => x ?? "";
SLaks
sumber
Terima kasih atas jawaban Anda, bisakah Anda menjelaskan sedikit lebih banyak tentang jawabannya, yang lamba, maksud saya codeFunc <string, string> PrefixTrimmer = x => x ?? ""; codeSaya baru dalam hal ini dan saya benar-benar tidak mengerti betul dokumen MSDN
Joe Dixon
2
@ Joe: x ?? ""adalah operator penggabungan nol. msdn.microsoft.com/en-us/library/ms173224.aspx
SLaks
In x => (something), xadalah parameter, dan (something)merupakan nilai kembalian`.
SLaks
2
@Hellfrost: Baca pertanyaannya. Dia tidak bertanya tentang fungsi sebaris gaya C ++. Saya tidak ingin membuat fungsi karena yang ini tidak akan membantu apa pun di bagian lain dari program saya, tetapi ini
SLaks
5

Anda dapat menggunakan Func yang merangkum metode yang memiliki satu parameter dan mengembalikan nilai tipe yang ditentukan oleh parameter TResult.

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


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

Tidak hanya metode di dalam, ini juga dapat digunakan di dalam kelas.

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);
    }
Deepak Mishra
sumber