답변:
ILSpy를 통해 컴파일 된 코드를 보면 실제로 두 참조에 차이가 있습니다. 이와 같은 간단한 프로그램의 경우 :
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는 다음과 같이 디 컴파일합니다.
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);
}
);
}
}
}
둘 다에 대한 IL 호출 스택을 살펴보면 명시 적 구현에는 훨씬 더 많은 호출이 있으며 생성 된 메소드가 작성됩니다.
.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'
암시 적 구현은 더 간결합니다.
.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
나는 일반적으로 람다 구문 을 선호합니다 . 당신이 그것을 볼 때, 그것은 유형이 무엇인지 알려줍니다. 이 표시되면 Console.WriteLine
IDE에 어떤 유형인지 묻어 야합니다. 물론,이 간단한 예에서는 명백하지만, 일반적인 경우에는 그리 많지 않을 수 있습니다.
두 가지 예를 들었을 때
List.ForEach(Console.WriteLine)
실제로 ForEach 루프에 WriteLine 메소드를 사용하도록 지시하고 있습니다.
List.ForEach(s => Console.WriteLine(s));
실제로 foreach가 호출 할 메소드를 정의한 다음 처리 할 항목을 알려줍니다.
따라서 하나의 라이너에 대해 호출 할 메소드가 이미 호출 된 메소드와 동일한 서명을 가지고 있다면 람다를 정의하지 않는 것이 좋습니다. 약간 더 읽기 쉽습니다.
호환되지 않는 람다가있는 메소드는 지나치게 복잡하지 않다고 가정 할 때 확실히 좋은 방법입니다.
첫 번째 줄을 선호하는 데는 매우 강력한 이유가 있습니다.
모든 대표는 Target
속성이 있으므로 인스턴스가 범위를 벗어난 후에도 델리게이트가 인스턴스 메소드를 참조 할 수 있습니다.
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;
null a1.WriteData();
이기 때문에 전화를 걸 수 없습니다 a1
. 그러나 action
문제없이 델리게이트를 호출 할 수 있으며 메소드를 호출해야하는 인스턴스에 대한 참조를 보유 4
하기 때문에 델리게이트가 인쇄됩니다 action
.
익명 컨텍스트가 인스턴스 컨텍스트에서 델리게이트로 전달 될 때 델리게이트는 명확하지 않지만 포함하는 클래스에 대한 참조를 계속 보유합니다.
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));
}
}
이 특정 경우에는 다음을 가정하는 것이 합리적입니다. .ForEach
델리게이트를 내부에 저장하지 않는다고 입니다.Container
모든 데이터 가 여전히 유지되고 . 그러나 그에 대한 보증은 없습니다. 델리게이트를받는 메소드는 델리게이트와 인스턴스를 무기한 보유 할 수 있습니다.
반면 정적 메소드는 참조 할 인스턴스가 없습니다. 다음은 인스턴스에 대한 암시 적 참조를 갖지 않습니다 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);
}
}