매개 변수가 하나만 있어도 람다 구문을 선호하는 이유가 있습니까?


14
List.ForEach(Console.WriteLine);

List.ForEach(s => Console.WriteLine(s));

나에게 차이는 순전히 미용 적이지만 왜 하나가 다른 것보다 선호 될 수있는 미묘한 이유가 있습니까?


내 경험상 두 번째 버전이 더 좋아 보일 때마다 일반적으로 문제의 방법의 이름이 잘못 되었기 때문입니다.
로마 라이너

답변:


23

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

이것은 빠른 스크래치 프로그램에서 코드의 릴리스 빌드이므로 추가 최적화를위한 여지가있을 수 있습니다. 그러나 이것은 Visual Studio의 기본 출력입니다.
Agent_9191

2
+1 람다 구문이 실제로 원시 메소드 호출을 익명 함수 <i> 이유없이 </ i> 래핑하기 때문입니다. 이는 무의미하므로 원시 메소드 그룹을 사용 가능한 경우 Func <> 매개 변수로 사용해야합니다.
Ed James

와우, 당신은 연구를 위해 녹색 진드기를 얻습니다!
Benjol

2

나는 일반적으로 람다 구문 선호합니다 . 당신이 그것을 볼 때, 그것은 유형이 무엇인지 알려줍니다. 이 표시되면 Console.WriteLineIDE에 어떤 유형인지 묻어 야합니다. 물론,이 간단한 예에서는 명백하지만, 일반적인 경우에는 그리 많지 않을 수 있습니다.


필요한 경우와의 일관성을 위해 labmda 구문을 선호합니다.
bunglestink

4
나는 C # 사람이 아니지만 람다 (JavaScript, Scheme 및 Haskell)와 함께 사용한 언어로 사람들은 아마도 반대의 조언을 줄 것입니다. 언어에 따라 스타일이 얼마나 좋은지 보여줍니다.
Tikhon Jelvis

어떤 식으로 유형을 알려줍니까? 확실히이 (가) 람다 매개 변수의 종류 만 약 명시 될 수는 지금까지이 상황에서 다 그렇게하는 것이 일반적인, 그리고 밤은에서
JK.

1

두 가지 예를 들었을 때

List.ForEach(Console.WriteLine) 

실제로 ForEach 루프에 WriteLine 메소드를 사용하도록 지시하고 있습니다.

List.ForEach(s => Console.WriteLine(s));

실제로 foreach가 호출 할 메소드를 정의한 다음 처리 할 항목을 알려줍니다.

따라서 하나의 라이너에 대해 호출 할 메소드가 이미 호출 된 메소드와 동일한 서명을 가지고 있다면 람다를 정의하지 않는 것이 좋습니다. 약간 더 읽기 쉽습니다.

호환되지 않는 람다가있는 메소드는 지나치게 복잡하지 않다고 가정 할 때 확실히 좋은 방법입니다.


1

첫 번째 줄을 선호하는 데는 매우 강력한 이유가 있습니다.

모든 대표는 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);
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.