Dalam pengalaman saya setiap kali versi kedua tampaknya lebih disukai biasanya karena penamaan metode yang buruk.
Roman Reiner
Jawaban:
23
Melihat kode yang dikompilasi melalui ILSpy, sebenarnya ada perbedaan dalam dua referensi. Untuk program sederhana seperti ini:
namespace ScratchLambda{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;internalclassProgram{privatestaticvoidMain(string[] args){varlist=Enumerable.Range(1,10).ToList();ExplicitLambda(list);ImplicitLambda(list);}privatestaticvoidImplicitLambda(List<int>list){list.ForEach(Console.WriteLine);}privatestaticvoidExplicitLambda(List<int>list){list.ForEach(s =>Console.WriteLine(s));}}}
ILSpy mendekompilasi sebagai:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ScratchLambda{internalclassProgram{privatestaticvoidMain(string[] args){List<int>list=Enumerable.Range(1,10).ToList<int>();Program.ExplicitLambda(list);Program.ImplicitLambda(list);}privatestaticvoidImplicitLambda(List<int>list){list.ForEach(newAction<int>(Console.WriteLine));}privatestaticvoidExplicitLambda(List<int>list){list.ForEach(delegate(int s){Console.WriteLine(s);});}}}
Jika Anda melihat tumpukan panggilan IL untuk keduanya, implementasi Eksplisit memiliki lebih banyak panggilan (dan membuat metode yang dihasilkan):
.method private hidebysig staticvoidExplicitLambda(class[mscorlib]System.Collections.Generic.List`1<int32>list) cil managed
{// Method begins at RVA 0x2093// Code size 36 (0x24).maxstack 8
IL_0000: ldarg.0
IL_0001: ldsfld class[mscorlib]System.Action`1<int32>ScratchLambda.Program::'CS$<>9__CachedAnonymousMethodDelegate1'
IL_0006: brtrue.s IL_0019
IL_0008: ldnull
IL_0009: ldftn voidScratchLambda.Program::'<ExplicitLambda>b__0'(int32)
IL_000f: newobj instance voidclass[mscorlib]System.Action`1<int32>::.ctor(object, native int)
IL_0014: stsfld class[mscorlib]System.Action`1<int32>ScratchLambda.Program::'CS$<>9__CachedAnonymousMethodDelegate1'
IL_0019: ldsfld class[mscorlib]System.Action`1<int32>ScratchLambda.Program::'CS$<>9__CachedAnonymousMethodDelegate1'
IL_001e: callvirt instance voidclass[mscorlib]System.Collections.Generic.List`1<int32>::ForEach(class[mscorlib]System.Action`1<!0>)
IL_0023: ret
}// end of method Program::ExplicitLambda.method private hidebysig staticvoid'<ExplicitLambda>b__0'(int32 s
) cil managed
{.custom instance void[mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()=(01000000)// Method begins at RVA 0x208b// Code size 7 (0x7).maxstack 8
IL_0000: ldarg.0
IL_0001: call void[mscorlib]System.Console::WriteLine(int32)
IL_0006: ret
}// end of method Program::'<ExplicitLambda>b__0'
Perhatikan bahwa ini adalah versi rilis kode dari program gores cepat, jadi mungkin ada ruang untuk pengoptimalan lebih lanjut. Tapi ini adalah output default dari Visual Studio.
Agent_9191
2
+1 Itu karena sintaks lambda sebenarnya membungkus panggilan metode mentah dalam fungsi anonim <i> tanpa alasan </i>. Ini sama sekali tidak ada gunanya, maka Anda harus menggunakan grup metode mentah sebagai parameter <> ketika fungsi itu tersedia.
Ed James
Wow, Anda mendapatkan tanda centang hijau, untuk penelitian!
Benjol
2
Saya lebih suka sintaks lambda secara umum . Ketika Anda melihat itu, maka itu memberi tahu Anda apa jenisnya. Ketika Anda melihat Console.WriteLine, Anda harus bertanya pada IDE tipe apa itu. Tentu saja, dalam contoh sepele ini, sudah jelas, tetapi dalam kasus umum, mungkin tidak terlalu banyak.
Saya lebih suka sintaks labmda untuk konsistensi dengan kasus-kasus di mana diperlukan.
bunglestink
4
Saya bukan orang C #, tetapi dalam bahasa yang saya gunakan dengan lambdas (JavaScript, Skema dan Haskell) orang mungkin akan memberi Anda saran yang berlawanan. Saya pikir itu hanya menunjukkan seberapa baik gaya tergantung pada bahasa.
Tikhon Jelvis
dengan cara apa ia memberi tahu Anda tipenya? tentu saja Anda dapat secara eksplisit tentang jenis parameter lambdas tetapi jauh dari umum untuk melakukan itu, dan tidak dilakukan dalam situasi ini
jk.
1
dengan dua contoh yang Anda berikan, mereka berbeda ketika Anda mengatakannya
List.ForEach(Console.WriteLine)
Anda sebenarnya memberi tahu ForEach Loop untuk menggunakan metode WriteLine
List.ForEach(s =>Console.WriteLine(s));
sebenarnya mendefinisikan metode yang akan memanggil foreach dan kemudian Anda mengatakan apa yang harus ditangani di sana.
jadi untuk satu baris sederhana jika metode Anda, Anda akan memanggil membawa tanda tangan yang sama dengan metode yang dipanggil sudah saya lebih suka tidak mendefinisikan lambda, saya pikir itu sedikit lebih mudah dibaca.
karena metode dengan lambda yang tidak kompatibel jelas merupakan cara yang baik untuk dilakukan, dengan asumsi mereka tidak terlalu rumit.
Ada alasan yang sangat kuat untuk memilih baris pertama.
Setiap delegasi memiliki Targetproperti, yang memungkinkan delegasi untuk merujuk pada metode instance, bahkan setelah instance sudah keluar dari ruang lingkup.
Kami tidak dapat menelepon a1.WriteData();karena a1tidak ada. Namun, kita dapat memanggil actiondelegasi tanpa masalah, dan itu akan dicetak 4, karena actionmenyimpan referensi ke instance yang harus dipanggil metode.
Ketika metode anonim dilewatkan sebagai delegasi dalam konteks instance, delegasi masih akan memiliki referensi ke kelas yang berisi, meskipun itu tidak jelas:
publicclassContainer{privateList<int> data =newList<int>(){1,2,3,4,5};publicvoidPrintItems(){//There is an implicit reference to an instance of Container here
data.ForEach(s =>Console.WriteLine(s));}}
Dalam kasus khusus ini, masuk akal untuk berasumsi bahwa .ForEachtidak menyimpan delegasi secara internal, yang berarti bahwa instance Containerdan semua datanya masih disimpan. Tetapi tidak ada jaminan untuk itu; metode menerima delegasi mungkin berpegang pada delegasi dan mesin virtual tanpa batas.
Metode statis, di sisi lain, tidak memiliki contoh untuk referensi. Berikut ini tidak akan memiliki referensi implisit ke instance dari Container:
publicclassContainer{privateList<int> data =newList<int>(){1,2,3,4,5};publicvoidPrintItems(){//Since Console.WriteLine is a static method, there is no implicit reference
data.ForEach(Console.WriteLine);}}
Jawaban:
Melihat kode yang dikompilasi melalui ILSpy, sebenarnya ada perbedaan dalam dua referensi. Untuk program sederhana seperti ini:
ILSpy mendekompilasi sebagai:
Jika Anda melihat tumpukan panggilan IL untuk keduanya, implementasi Eksplisit memiliki lebih banyak panggilan (dan membuat metode yang dihasilkan):
sementara implementasi Implisit lebih ringkas:
sumber
Saya lebih suka sintaks lambda secara umum . Ketika Anda melihat itu, maka itu memberi tahu Anda apa jenisnya. Ketika Anda melihat
Console.WriteLine
, Anda harus bertanya pada IDE tipe apa itu. Tentu saja, dalam contoh sepele ini, sudah jelas, tetapi dalam kasus umum, mungkin tidak terlalu banyak.sumber
dengan dua contoh yang Anda berikan, mereka berbeda ketika Anda mengatakannya
Anda sebenarnya memberi tahu ForEach Loop untuk menggunakan metode WriteLine
sebenarnya mendefinisikan metode yang akan memanggil foreach dan kemudian Anda mengatakan apa yang harus ditangani di sana.
jadi untuk satu baris sederhana jika metode Anda, Anda akan memanggil membawa tanda tangan yang sama dengan metode yang dipanggil sudah saya lebih suka tidak mendefinisikan lambda, saya pikir itu sedikit lebih mudah dibaca.
karena metode dengan lambda yang tidak kompatibel jelas merupakan cara yang baik untuk dilakukan, dengan asumsi mereka tidak terlalu rumit.
sumber
Ada alasan yang sangat kuat untuk memilih baris pertama.
Setiap delegasi memiliki
Target
properti, yang memungkinkan delegasi untuk merujuk pada metode instance, bahkan setelah instance sudah keluar dari ruang lingkup.Kami tidak dapat menelepon
a1.WriteData();
karenaa1
tidak ada. Namun, kita dapat memanggilaction
delegasi tanpa masalah, dan itu akan dicetak4
, karenaaction
menyimpan referensi ke instance yang harus dipanggil metode.Ketika metode anonim dilewatkan sebagai delegasi dalam konteks instance, delegasi masih akan memiliki referensi ke kelas yang berisi, meskipun itu tidak jelas:
Dalam kasus khusus ini, masuk akal untuk berasumsi bahwa
.ForEach
tidak menyimpan delegasi secara internal, yang berarti bahwa instanceContainer
dan semua datanya masih disimpan. Tetapi tidak ada jaminan untuk itu; metode menerima delegasi mungkin berpegang pada delegasi dan mesin virtual tanpa batas.Metode statis, di sisi lain, tidak memiliki contoh untuk referensi. Berikut ini tidak akan memiliki referensi implisit ke instance dari
Container
:sumber