delegasikan kata kunci vs. notasi lambda

Jawaban:

140

Jawaban singkat: tidak.

Jawaban yang lebih panjang yang mungkin tidak relevan:

  • Jika Anda menetapkan lambda ke jenis delegasi (seperti Funcatau Action) Anda akan mendapatkan delegasi anonim.
  • Jika Anda menetapkan lambda ke jenis Ekspresi, Anda akan mendapatkan pohon ekspresi alih-alih delegasi anonim. Pohon ekspresi kemudian dapat dikompilasi ke delegasi anonim.

Sunting: Berikut ini beberapa tautan untuk Ekspresi.

  • System.Linq.Expression.Expression (TDelegate) (mulai di sini).
  • Linq dalam memori dengan delegasi (seperti System.Func) menggunakan System.Linq.Enumerable . Linq to SQL (dan apa pun) dengan ekspresi menggunakan System.Linq.Queryable . Lihat parameter pada metode tersebut.
  • Sebuah Penjelasan dari ScottGu . Singkatnya, memori Linq akan menghasilkan beberapa metode anonim untuk menyelesaikan kueri Anda. Linq to SQL akan menghasilkan pohon ekspresi yang mewakili permintaan dan kemudian menerjemahkan pohon itu ke T-SQL. Linq to Entities akan menghasilkan pohon ekspresi yang mewakili permintaan dan kemudian menerjemahkan pohon itu ke dalam platform yang sesuai SQL.
Amy B
sumber
3
Jenis Ekspresi? Ini kedengarannya seperti wilayah baru bagi saya. Di mana saya bisa mengetahui lebih banyak tentang tipe Ekspresi dan menggunakan pohon ekspresi di C #?
MojoFilter
2
Jawaban yang lebih panjang - ada alasan fiddly mengapa mereka dapat dikonversi ke tipe delegasi yang berbeda juga :)
Jon Skeet
Perhatikan bahwa lambda hanya dapat ditugaskan ke tipe Ekspresi, jika itu adalah lambda ekspresi.
Micha Wiedenmann
125

Saya suka jawaban Amy, tetapi saya pikir saya akan menjadi sangat baik. Pertanyaannya mengatakan, "Setelah dikompilasi" - yang menunjukkan bahwa kedua ekspresi telah dikompilasi. Bagaimana mereka bisa mengkompilasi, tetapi dengan satu dikonversi menjadi delegasi dan satu ke pohon ekspresi? Itu rumit - Anda harus menggunakan fitur lain dari metode anonim; satu-satunya yang tidak dibagikan oleh ekspresi lambda. Jika Anda menentukan metode anonim tanpa menentukan daftar parameter sama sekali, itu kompatibel dengan semua jenis delegasi yang kembali batal dan tanpa outparameter apa pun . Berbekal pengetahuan ini, kita harus dapat membangun dua kelebihan untuk membuat ekspresi yang sama sekali tidak ambigu tetapi sangat berbeda.

Tapi bencana melanda! Paling tidak dengan C # 3.0, Anda tidak bisa mengonversi ekspresi lambda dengan badan blok menjadi ekspresi - juga tidak bisa mengonversi ekspresi lambda dengan tugas di tubuh (bahkan jika itu digunakan sebagai nilai balik). Ini dapat berubah dengan C # 4.0 dan .NET 4.0, yang memungkinkan lebih banyak diekspresikan dalam pohon ekspresi. Jadi dengan kata lain, dengan contoh yang diberikan MojoFilter, keduanya hampir selalu akan dikonversi ke hal yang sama. (Lebih detail dalam satu menit.)

Kita dapat menggunakan trik parameter delegasi jika kita mengubah sedikit tubuh:

using System;
using System.Linq.Expressions;

public class Test
{
    static void Main()
    {
        int x = 0;
        Foo( () => x );
        Foo( delegate { return x; } );
    }

    static void Foo(Func<int, int> action)
    {
        Console.WriteLine("I suspect the anonymous method...");
    }

    static void Foo(Expression<Func<int>> func)
    {
        Console.WriteLine("I suspect the lambda expression...");
    }
}

Tapi tunggu! Kita dapat membedakan antara keduanya bahkan tanpa menggunakan pohon ekspresi, jika kita cukup cerdik. Contoh di bawah ini menggunakan aturan resolusi kelebihan beban (dan trik pencocokan delegasi anonim) ...

using System;
using System.Linq.Expressions;

public class Base
{
    public void Foo(Action action)
    {
        Console.WriteLine("I suspect the lambda expression...");
    }
}

public class Derived : Base
{
    public void Foo(Action<int> action)
    {
        Console.WriteLine("I suspect the anonymous method...");
    }
}

class Test
{
    static void Main()
    {
        Derived d = new Derived();
        int x = 0;
        d.Foo( () => { x = 0; } );
        d.Foo( delegate { x = 0; } );
    }
}

Aduh. Ingat anak-anak, setiap kali Anda membebani metode yang diwarisi dari kelas dasar, anak kucing kecil mulai menangis.

Jon Skeet
sumber
9
Saya mengeluarkan popcorn dan membaca semuanya. Itulah beberapa perbedaan yang mungkin tidak akan pernah terpikirkan oleh saya bahkan jika saya menatapnya tepat di wajah.
MojoFilter
27
Saya tahu beberapa hal ini, tetapi saya harus memberi selamat kepada Anda atas kemampuan Anda untuk mengomunikasikannya kepada manusia.
Amy B
1
Untuk siapa saja yang tertarik dengan perubahan pada .NET 4.0 (berdasarkan CTP) - lihat marcgravell.blogspot.com/2008/11/future-expressions.html . Perhatikan bahwa C # 4.0 tidak melakukan sesuatu yang baru sejauh yang saya tahu.
Marc Gravell
4
Jon, dasar kau. Erik, untuk menjadi fanboi Skeet sejati, Anda harus berlangganan rss stack overflow-nya seperti saya. Cukup masukkan stackoverflow.com/users/22656 ke pembaca feed Anda.
Paul Batum
3
@RoyiNamir: Jika Anda menggunakan metode anonim tanpa daftar parameter, itu kompatibel dengan tipe delegasi apa pun dengan parameter non-ref / out, asalkan tipe return kompatibel. Pada dasarnya Anda mengatakan "Saya tidak peduli dengan parameter". Perhatikan bahwa delegate { ... }adalah tidak sama dengan delegate() { ... }- yang terakhir ini hanya kompatibel dengan jenis parameterless delegasi.
Jon Skeet
2

Dalam dua contoh di atas tidak ada perbedaan, nol.

Ekspresi:

() => { x = 0 }

adalah ekspresi Lambda dengan tubuh pernyataan, sehingga tidak dapat dikompilasi sebagai pohon ekspresi. Bahkan ia tidak dapat dikompilasi karena memerlukan titik koma setelah 0:

() => { x = 0; } // Lambda statement body
() => x = 0      // Lambda expression body, could be an expression tree. 
Olmo
sumber
6
Tentunya itu berarti ada perbedaan "satu akan dikompilasi, yang lain tidak akan";)
Jon Skeet
2

Amy B benar. Perhatikan bahwa mungkin ada keuntungan menggunakan pohon ekspresi. LINQ ke SQL akan memeriksa pohon ekspresi dan mengubahnya menjadi SQL.

Anda juga dapat bermain trik dengan lamdas dan pohon ekspresi untuk secara efektif menyampaikan nama anggota kelas ke kerangka kerja dengan cara yang aman untuk refactoring. Moq adalah contoh dari ini.

Daniel Plaisted
sumber
-1

Ada perbedaan

Contoh:

var mytask = Task.Factory.StartNew(() =>
{
    Thread.Sleep(5000);
    return 2712;
});
mytask.ContinueWith(delegate
{
    _backgroundTask.ContinueTask(() =>lblPercent.Content = mytask.Result.ToString(CultureInfo.InvariantCulture));
});   

Dan saya ganti dengan lambda: (kesalahan)

var mytask = Task.Factory.StartNew(() =>
{
    Thread.Sleep(5000);
    return 2712;
});
mytask.ContinueWith(()=>
{
    _backgroundTask.ContinueTask(() =>lblPercent.Content = mytask.Result.ToString(CultureInfo.InvariantCulture));
});
Di Bình Trọng
sumber
Salah ~ lambda gagal hanya karena tanda tangan parameter metode tidak cocok.
Jack Wang
-1

Beberapa dasar di sini.

Ini adalah metode anonim

(string testString) => { Console.WriteLine(testString); };

Karena metode anonim tidak memiliki nama, kami memerlukan delegasi tempat kami dapat menetapkan kedua metode atau ekspresi ini. misalnya

delegate void PrintTestString(string testString); // declare a delegate

PrintTestString print = (string testString) => { Console.WriteLine(testString); }; 
print();

Sama dengan ekspresi lambda. Biasanya kita membutuhkan delegasi untuk menggunakannya

s => s.Age > someValue && s.Age < someValue    // will return true/false

Kita dapat menggunakan delegate func untuk menggunakan ungkapan ini.

Func< Student,bool> checkStudentAge = s => s.Age > someValue && s.Age < someValue ;

bool result = checkStudentAge ( Student Object);
Yogesh Prajapati
sumber