Ekspresi C # Lambda: Mengapa saya harus menggunakannya?

310

Saya telah dengan cepat membaca dokumentasi Microsoft Lambda Expression .

Contoh seperti ini telah membantu saya untuk memahami dengan lebih baik:

delegate int del(int i);
del myDelegate = x => x * x;
int j = myDelegate(5); //j = 25

Namun, saya tidak mengerti mengapa ini merupakan inovasi. Itu hanya metode yang mati ketika "variabel metode" berakhir, kan? Mengapa saya harus menggunakan ini daripada metode nyata?

Patrick Desjardins
sumber
3
Bagi Anda yang datang ke halaman ini dan tidak tahu apa yang delegateada di C #, saya sangat menyarankan membaca ini sebelum membaca sisa halaman ini: stackoverflow.com/questions/2082615/…
Kolob Canyon

Jawaban:

282

Ekspresi Lambda adalah sintaksis yang lebih sederhana untuk delegasi anonim dan dapat digunakan di mana saja delegasi anonim dapat digunakan. Namun, yang terjadi adalah sebaliknya; ekspresi lambda dapat dikonversi ke pohon ekspresi yang memungkinkan banyak keajaiban seperti LINQ ke SQL.

Berikut ini adalah contoh ekspresi LINQ to Objects menggunakan delegasi anonim kemudian ekspresi lambda untuk menunjukkan betapa lebih mudahnya mata mereka:

// anonymous delegate
var evens = Enumerable
                .Range(1, 100)
                .Where(delegate(int x) { return (x % 2) == 0; })
                .ToList();

// lambda expression
var evens = Enumerable
                .Range(1, 100)
                .Where(x => (x % 2) == 0)
                .ToList();

Ekspresi Lambda dan delegasi anonim memiliki keuntungan daripada menulis fungsi terpisah: mereka menerapkan penutupan yang memungkinkan Anda untuk melewati status lokal ke fungsi tanpa menambahkan parameter ke fungsi atau membuat objek sekali pakai.

Pohon ekspresi adalah fitur baru yang sangat kuat dari C # 3.0 yang memungkinkan API untuk melihat struktur ekspresi alih-alih hanya mendapatkan referensi ke metode yang dapat dieksekusi. API hanya perlu membuat parameter delegasi menjadi Expression<T>parameter dan kompiler akan menghasilkan pohon ekspresi dari lambda alih-alih delegasi anonim:

void Example(Predicate<int> aDelegate);

disebut seperti:

Example(x => x > 5);

menjadi:

void Example(Expression<Predicate<int>> expressionTree);

Yang terakhir akan melewati representasi pohon sintaksis abstrak yang menggambarkan ekspresi x > 5. LINQ ke SQL bergantung pada perilaku ini untuk dapat mengubah ekspresi C # ke ekspresi SQL yang diinginkan untuk memfilter / memesan / dll di sisi server.

Neil Williams
sumber
1
Tanpa penutup, Anda dapat menggunakan metode statis sebagai panggilan balik, tetapi Anda masih harus mendefinisikan metode tersebut di beberapa kelas, hampir secara pasti meningkatkan cakupan metode tersebut di luar penggunaan yang dimaksudkan.
DK.
10
FWIW, Anda dapat memiliki penutupan dengan delegasi anonim, sehingga Anda tidak benar-benar membutuhkan lambdas untuk itu. Lambdas hanya jauh lebih mudah dibaca daripada delegasi anonim, tanpa yang menggunakan Linq akan membuat mata Anda berdarah.
Benjol
138

Fungsi dan ekspresi anonim berguna untuk metode satu kali yang tidak mendapat manfaat dari kerja ekstra yang diperlukan untuk membuat metode lengkap.

Pertimbangkan contoh ini:

 string person = people.Find(person => person.Contains("Joe"));

melawan

 public string FindPerson(string nameContains, List<string> persons)
 {
     foreach (string person in persons)
         if (person.Contains(nameContains))
             return person;
     return null;
 }

Ini setara secara fungsional.

Joseph Daigle
sumber
8
Bagaimana metode Find () didefinisikan untuk menangani ekspresi lambda ini?
Patrick Desjardins
3
Predikat <T> adalah yang diharapkan oleh metode Temukan.
Darren Kopp
1
Karena ekspresi lambda saya cocok dengan kontrak untuk Predikat <T>, metode Find () menerimanya.
Joseph Daigle
maksud Anda "string person = people.Find (persons => persons.Contains (" Joe "));
Gern Blanston
5
@FKCoder, tidak, dia tidak, meskipun mungkin lebih jelas jika dia mengatakan "string person = people.Find (p => p.Contains (" Joe "));"
Benjol
84

Saya menemukan mereka berguna dalam situasi ketika saya ingin menyatakan pawang untuk acara kontrol tertentu, menggunakan kontrol lain. Untuk melakukannya secara normal, Anda harus menyimpan referensi kontrol di bidang kelas sehingga Anda dapat menggunakannya dalam metode yang berbeda dari yang dibuat.

private ComboBox combo;
private Label label;

public CreateControls()
{
    combo = new ComboBox();
    label = new Label();
    //some initializing code
    combo.SelectedIndexChanged += new EventHandler(combo_SelectedIndexChanged);
}

void combo_SelectedIndexChanged(object sender, EventArgs e)
{
    label.Text = combo.SelectedValue;
}

terima kasih untuk ekspresi lambda Anda dapat menggunakannya seperti ini:

public CreateControls()
{
    ComboBox combo = new ComboBox();
    Label label = new Label();
    //some initializing code
    combo.SelectedIndexChanged += (s, e) => {label.Text = combo.SelectedValue;};
}

Jauh lebih mudah.

agnieszka
sumber
Pada contoh pertama, mengapa tidak mengirim pengirim dan mendapatkan nilainya?
Andrew
@Andrew: Dalam contoh sederhana ini tidak perlu menggunakan pengirim, karena hanya ada satu komponen yang dipermasalahkan dan menggunakan bidang secara langsung menyimpan gips, yang meningkatkan kejelasan. Dalam skenario dunia nyata, saya pribadi lebih suka menggunakan pengirimnya juga. Biasanya saya menggunakan satu pengendali acara untuk beberapa acara, jika mungkin dan saya harus mengidentifikasi pengirim yang sebenarnya.
Chris Tophski
35

Lambda membersihkan sintaksis anonim delegasi C # 2.0 ... misalnya

Strings.Find(s => s == "hello");

Dilakukan di C # 2.0 seperti ini:

Strings.Find(delegate(String s) { return s == "hello"; });

Secara fungsional, mereka melakukan hal yang sama persis, hanya sintaks yang jauh lebih ringkas.

FlySwat
sumber
3
Mereka tidak cukup hal yang sama - seperti yang ditunjukkan @ Neil Williams, Anda dapat mengekstrak AST sebuah lambdas' menggunakan pohon ekspresi, sedangkan metode anonim tidak dapat digunakan dengan cara yang sama.
ls
ini adalah salah satu dari banyak kelebihan lambda lainnya. Ini membantu memahami kode lebih baik daripada metode anonim. tentu saja itu bukan maksud menciptakan lambda, tetapi ini adalah skenario di mana ia dapat digunakan lebih sering.
Guruji
29

Ini hanyalah salah satu cara menggunakan ekspresi lambda. Anda dapat menggunakan ekspresi lambda di mana saja Anda bisa menggunakan delegasi. Ini memungkinkan Anda melakukan hal-hal seperti ini:

List<string> strings = new List<string>();
strings.Add("Good");
strings.Add("Morning")
strings.Add("Starshine");
strings.Add("The");
strings.Add("Earth");
strings.Add("says");
strings.Add("hello");

strings.Find(s => s == "hello");

Kode ini akan mencari daftar entri yang cocok dengan kata "halo". Cara lain untuk melakukannya adalah dengan mengirimkan delegasi ke metode Find, seperti ini:

List<string> strings = new List<string>();
strings.Add("Good");
strings.Add("Morning")
strings.Add("Starshine");
strings.Add("The");
strings.Add("Earth");
strings.Add("says");
strings.Add("hello");

private static bool FindHello(String s)
{
    return s == "hello";
}

strings.Find(FindHello);

EDIT :

Dalam C # 2.0, ini bisa dilakukan menggunakan sintaks delegasi anonim:

  strings.Find(delegate(String s) { return s == "hello"; });

Lambda secara signifikan membersihkan sintaks itu.

Scott Dorman
sumber
2
@ Jonathan Holland: Terima kasih atas edit dan menambahkan sintaks delegasi anonim. Ini melengkapi contoh dengan baik.
Scott Dorman
apa itu delegasi anonim? // maaf saya baru
mengenal
1
@ Hackman, pikirkan delegasi anonim sebagai fungsi yang tidak memiliki "nama". Anda masih mendefinisikan fungsi, yang dapat memiliki input dan output, tetapi karena itu adalah nama yang Anda tidak dapat merujuknya secara langsung. Dalam kode yang ditunjukkan di atas, Anda mendefinisikan metode (yang mengambil a stringdan mengembalikan a bool) sebagai parameter ke Findmetode itu sendiri.
Scott Dorman
22

Microsoft telah memberi kami cara yang lebih bersih dan lebih nyaman untuk membuat delegasi anonim yang disebut ekspresi Lambda. Namun, tidak banyak perhatian diberikan pada bagian ekspresi dari pernyataan ini. Microsoft merilis seluruh namespace, System.Linq.Expressions , yang berisi kelas untuk membuat pohon ekspresi berdasarkan pada ekspresi lambda. Pohon ekspresi terdiri dari objek yang mewakili logika. Misalnya, x = y + z adalah ekspresi yang mungkin menjadi bagian dari pohon ekspresi di .Net. Pertimbangkan contoh (sederhana) berikut ini:

using System;
using System.Linq;
using System.Linq.Expressions;


namespace ExpressionTreeThingy
{
    class Program
    {
        static void Main(string[] args)
        {
            Expression<Func<int, int>> expr = (x) => x + 1; //this is not a delegate, but an object
            var del = expr.Compile(); //compiles the object to a CLR delegate, at runtime
            Console.WriteLine(del(5)); //we are just invoking a delegate at this point
            Console.ReadKey();
        }
    }
}

Contoh ini sepele. Dan saya yakin Anda berpikir, "Ini tidak berguna karena saya bisa langsung membuat delegasi daripada membuat ekspresi dan mengkompilasinya saat runtime". Dan Anda benar. Tetapi ini memberikan dasar untuk pohon ekspresi. Ada sejumlah ekspresi yang tersedia di ruang nama Ekspresi, dan Anda bisa membuatnya sendiri. Saya pikir Anda dapat melihat bahwa ini mungkin berguna ketika Anda tidak tahu persis apa yang harus algoritma pada desain atau waktu kompilasi. Saya melihat contoh di suatu tempat untuk menggunakan ini untuk menulis kalkulator ilmiah. Anda juga bisa menggunakannya untuk sistem Bayesian , atau untuk pemrograman genetik(AI). Beberapa kali dalam karir saya, saya harus menulis fungsionalitas seperti Excel yang memungkinkan pengguna untuk memasukkan ekspresi sederhana (penambahan, subtrasi, dll) untuk beroperasi pada data yang tersedia. Di pre-.Net 3.5 saya harus menggunakan beberapa bahasa scripting eksternal untuk C #, atau harus menggunakan fungsi pemancar kode dalam refleksi untuk membuat. Net kode dengan cepat. Sekarang saya akan menggunakan pohon ekspresi.

Jason Jackson
sumber
12

Menghemat harus memiliki metode yang hanya digunakan sekali di tempat tertentu dari yang didefinisikan jauh dari tempat mereka digunakan. Penggunaan yang baik adalah sebagai pembanding untuk algoritma umum seperti pengurutan, di mana Anda kemudian dapat mendefinisikan fungsi pengurutan khusus tempat Anda memohon pengurutan tersebut daripada memaksa Anda untuk mencari di tempat lain untuk melihat apa yang sedang Anda sortir.

Dan itu bukan inovasi. LISP telah memiliki fungsi lambda selama sekitar 30 tahun atau lebih.

workmad3
sumber
6

Anda juga dapat menemukan penggunaan ekspresi lambda dalam menulis kode generik untuk bertindak berdasarkan metode Anda.

Misalnya: Fungsi generik untuk menghitung waktu yang diambil oleh pemanggilan metode. (yaitu Actiondi sini)

public static long Measure(Action action)
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    action();
    sw.Stop();
    return sw.ElapsedMilliseconds;
}

Dan Anda dapat memanggil metode di atas menggunakan ekspresi lambda sebagai berikut,

var timeTaken = Measure(() => yourMethod(param));

Ekspresi memungkinkan Anda untuk mendapatkan nilai balik dari metode Anda dan juga param

var timeTaken = Measure(() => returnValue = yourMethod(param, out outParam));
Gunasekaran
sumber
5

Ekspresi Lambda adalah cara ringkas untuk mewakili metode anonim. Baik metode anonim dan ekspresi Lambda memungkinkan Anda menentukan penerapan metode inline, namun, metode anonim secara eksplisit mengharuskan Anda untuk menentukan jenis parameter dan jenis pengembalian untuk suatu metode. Ekspresi Lambda menggunakan fitur tipe inferensi dari C # 3.0 yang memungkinkan kompiler untuk menyimpulkan tipe variabel berdasarkan konteks. Ini sangat nyaman karena menghemat banyak pengetikan!

Vijesh VP
sumber
5

Ekspresi lambda seperti metode anonim yang ditulis sebagai pengganti instance delegasi.

delegate int MyDelagate (int i);
MyDelagate delSquareFunction = x => x * x;

Pertimbangkan ungkapan lambda x => x * x;

Nilai parameter input adalah x (di sebelah kiri =>)

Logika fungsi adalah x * x (di sebelah kanan =>)

Kode ekspresi lambda dapat menjadi blok pernyataan alih-alih ekspresi.

x => {return x * x;};

Contoh

Catatan: Funcadalah delegasi generik yang telah ditentukan sebelumnya.

    Console.WriteLine(MyMethod(x => "Hi " + x));

    public static string MyMethod(Func<string, string> strategy)
    {
        return strategy("Lijo").ToString();
    }

Referensi

  1. Bagaimana cara delegasi & antarmuka digunakan secara bergantian?
LCJ
sumber
4

Sering kali, Anda hanya menggunakan fungsi di satu tempat, jadi membuat metode hanya mengacaukan kelas.

Darren Kopp
sumber
3

Ini cara mengambil operasi kecil dan meletakkannya sangat dekat dengan tempat itu digunakan (tidak seperti mendeklarasikan variabel dekat dengan titik penggunaannya). Ini seharusnya membuat kode Anda lebih mudah dibaca. Dengan menganonimkan ekspresi, Anda juga membuatnya lebih sulit bagi seseorang untuk memecahkan kode klien Anda jika fungsinya digunakan di tempat lain dan dimodifikasi untuk "meningkatkan" itu.

Demikian pula, mengapa Anda perlu menggunakan foreach? Anda dapat melakukan segala sesuatu di foreach dengan polos untuk loop atau hanya menggunakan IEnumerable secara langsung. Jawab: Anda tidak membutuhkannya tetapi itu membuat kode Anda lebih mudah dibaca.

alas tiang
sumber
0

Inovasi dalam jenis keamanan dan transparansi. Meskipun Anda tidak mendeklarasikan jenis ekspresi lambda, mereka disimpulkan, dan dapat digunakan oleh pencarian kode, analisis statis, alat refactoring, dan refleksi runtime.

Sebagai contoh, sebelum Anda mungkin menggunakan SQL dan bisa mendapatkan serangan injeksi SQL, karena seorang hacker melewati sebuah string di mana angka biasanya diharapkan. Sekarang Anda akan menggunakan ekspresi lambda LINQ, yang dilindungi dari itu.

Membangun API LINQ pada delegasi murni tidak dimungkinkan, karena memerlukan penggabungan pohon ekspresi sebelum mengevaluasi mereka.

Pada tahun 2016 sebagian besar bahasa populer memiliki dukungan ekspresi lambda , dan C # adalah salah satu pelopor dalam evolusi ini di antara bahasa-bahasa utama arus utama.

battlmonstr
sumber
0

Ini mungkin penjelasan terbaik tentang mengapa menggunakan ekspresi lambda -> https://youtu.be/j9nj5dTo54Q

Singkatnya, ini untuk meningkatkan keterbacaan kode, mengurangi kemungkinan kesalahan dengan menggunakan kembali daripada mereplikasi kode, dan meningkatkan optimasi yang terjadi di belakang layar.

coffeeeee
sumber
0

Manfaat terbesar dari ekspresi lambda dan fungsi anonim adalah kenyataan bahwa mereka memungkinkan klien (programmer) dari suatu perpustakaan / kerangka kerja untuk menyuntikkan fungsionalitas dengan menggunakan kode dalam perpustakaan / kerangka kerja yang diberikan (karena itu adalah LINQ, ASP.NET Core dan banyak lainnya) dengan cara yang metode biasa tidak bisa. Namun, kekuatan mereka tidak jelas untuk satu programmer aplikasi tetapi untuk orang yang membuat perpustakaan yang nantinya akan digunakan oleh orang lain yang ingin mengkonfigurasi perilaku kode perpustakaan atau orang yang menggunakan perpustakaan. Jadi konteks efektif menggunakan ekspresi lambda adalah penggunaan / pembuatan perpustakaan / kerangka kerja.

Juga karena mereka menggambarkan kode penggunaan satu kali, mereka tidak harus menjadi anggota kelas di mana itu akan menyebabkan lebih banyak kompleksitas kode. Bayangkan harus mendeklarasikan kelas dengan fokus tidak jelas setiap kali kita ingin mengkonfigurasi operasi objek kelas.

Grigoris Dimitroulakos
sumber