List.ForEach(Console.WriteLine);
List.ForEach(s => Console.WriteLine(s));
Bagi saya, perbedaannya murni kosmetik, tetapi adakah alasan halus mengapa yang satu lebih disukai daripada yang lain?
List.ForEach(Console.WriteLine);
List.ForEach(s => Console.WriteLine(s));
Bagi saya, perbedaannya murni kosmetik, tetapi adakah alasan halus mengapa yang satu lebih disukai daripada yang lain?
Jawaban:
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;
internal class Program
{
private static void Main(string[] args)
{
var list = Enumerable.Range(1, 10).ToList();
ExplicitLambda(list);
ImplicitLambda(list);
}
private static void ImplicitLambda(List<int> list)
{
list.ForEach(Console.WriteLine);
}
private static void ExplicitLambda(List<int> list)
{
list.ForEach(s => Console.WriteLine(s));
}
}
}
ILSpy mendekompilasi sebagai:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ScratchLambda
{
internal class Program
{
private static void Main(string[] args)
{
List<int> list = Enumerable.Range(1, 10).ToList<int>();
Program.ExplicitLambda(list);
Program.ImplicitLambda(list);
}
private static void ImplicitLambda(List<int> list)
{
list.ForEach(new Action<int>(Console.WriteLine));
}
private static void ExplicitLambda(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 static
void ExplicitLambda (
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 void ScratchLambda.Program::'<ExplicitLambda>b__0'(int32)
IL_000f: newobj instance void class [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 void class [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 static
void '<ExplicitLambda>b__0' (
int32 s
) cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01 00 00 00
)
// 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'
sementara implementasi Implisit lebih ringkas:
.method private hidebysig static
void ImplicitLambda (
class [mscorlib]System.Collections.Generic.List`1<int32> list
) cil managed
{
// Method begins at RVA 0x2077
// Code size 19 (0x13)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldnull
IL_0002: ldftn void [mscorlib]System.Console::WriteLine(int32)
IL_0008: newobj instance void class [mscorlib]System.Action`1<int32>::.ctor(object, native int)
IL_000d: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::ForEach(class [mscorlib]System.Action`1<!0>)
IL_0012: ret
} // end of method Program::ImplicitLambda
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.
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 Target
properti, yang memungkinkan delegasi untuk merujuk pada metode instance, bahkan setelah instance sudah keluar dari ruang lingkup.
public class A {
public int Data;
public void WriteData() {
Console.WriteLine(this.Data);
}
}
var a1 = new A() {Data=4};
Action action = a1.WriteData;
a1 = null;
Kami tidak dapat menelepon a1.WriteData();
karena a1
tidak ada. Namun, kita dapat memanggil action
delegasi tanpa masalah, dan itu akan dicetak 4
, karena action
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:
public class Container {
private List<int> data = new List<int>() {1,2,3,4,5};
public void PrintItems() {
//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 .ForEach
tidak menyimpan delegasi secara internal, yang berarti bahwa instance Container
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
:
public class Container {
private List<int> data = new List<int>() {1,2,3,4,5};
public void PrintItems() {
//Since Console.WriteLine is a static method, there is no implicit reference
data.ForEach(Console.WriteLine);
}
}