List.ForEach(Console.WriteLine);
List.ForEach(s => Console.WriteLine(s));
Bana göre fark tamamen kozmetiktir, ancak birinin diğerine tercih edilmesinin ince nedenleri var mı?
List.ForEach(Console.WriteLine);
List.ForEach(s => Console.WriteLine(s));
Bana göre fark tamamen kozmetiktir, ancak birinin diğerine tercih edilmesinin ince nedenleri var mı?
Yanıtlar:
Derlenmiş koda ILSpy aracılığıyla bakıldığında, aslında iki referansta bir fark vardır. Bunun gibi basit bir program için:
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 şu şekilde ayrıştırır:
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);
}
);
}
}
}
Her ikisi için de IL çağrı yığınına bakarsanız, Explicit uygulaması çok daha fazla çağrıya sahiptir (ve oluşturulan bir yöntem oluşturur):
.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'
Örtük uygulama daha özlü olsa da:
.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
Genel olarak lambda sözdizimini tercih ederim . Bunu gördüğünüzde, türün ne olduğunu size söyler. Gördüğünüzde Console.WriteLine
, IDE'ye ne tür olduğunu sormanız gerekir. Tabii ki, bu önemsiz örnekte açıktır, ancak genel durumda, çok fazla olmayabilir.
verdiğiniz iki örnekle,
List.ForEach(Console.WriteLine)
aslında ForEach Loop'a WriteLine yöntemini kullanmasını söylüyorsunuz
List.ForEach(s => Console.WriteLine(s));
aslında foreach'in arayacağı bir yöntem tanımlamakta ve o zaman orada ne işleneceğini söylüyorsunuz.
yani basit bir gömlekleri için eğer yöntemi arayacaksanız zaten imzalanan yöntemle aynı imzayı taşıyorsa ben lambda tanımlamak için tercih etmem, biraz daha okunabilir düşünüyorum.
çünkü uyumsuz lambdaları olan yöntemler, aşırı derecede karmaşık olmadıklarını varsayarak, kesinlikle iyi bir yoldur.
İlk çizgiyi tercih etmek için çok güçlü bir neden var.
Her delege, Target
delegelerin örnek kapsam dışına çıktıktan sonra bile örnek yöntemlerine başvurmalarını sağlayan bir özelliğe sahiptir.
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;
Arayamayız a1.WriteData();
çünkü a1
null. Ancak, action
temsilcisini sorunsuz bir şekilde çağırabiliriz ve yazdırılır 4
, çünkü action
yöntemin çağrılması gereken örneğe başvuruda bulunur.
Anonim yöntemler bir örnek bağlamında temsilci olarak iletildiğinde, temsilci, açık olmasa da, yine de içeren sınıfa başvuruda bulunur:
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));
}
}
Bu özel durumda, .ForEach
delegeyi dahili olarak saklamadığını varsaymak mantıklıdır; bu, örneğinin Container
ve tüm verilerinin hala saklandığı anlamına gelir. Ama bunun garantisi yok; temsilci alan yöntem temsilci ve örnek üzerinde süresiz olarak bekletilebilir.
Statik yöntemlerin ise referans gösterecek bir örneği yoktur. Aşağıdakilerin örneği için örtük bir başvurusu olmayacaktır 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);
}
}