[c#] 일부 C # 람다식이 정적 메서드로 컴파일되는 이유는 무엇입니까?

아래 코드에서 볼 수 있듯이 Action<>객체를 변수로 선언했습니다 .

이 작업 메서드 대리자가 정적 메서드처럼 작동하는 이유를 알려주세요.

true다음 코드에서 반환 되는 이유는 무엇 입니까?

암호:

public static void Main(string[] args)
{
    Action<string> actionMethod = s => { Console.WriteLine("My Name is " + s); };

    Console.WriteLine(actionMethod.Method.IsStatic);

    Console.Read();
}

산출:

샘플 출력 예



답변

이는 폐쇄가 없기 때문일 가능성이 높습니다. 예를 들면 다음과 같습니다.

int age = 25;
Action<string> withClosure = s => Console.WriteLine("My name is {0} and I am {1} years old", s, age);
Action<string> withoutClosure = s => Console.WriteLine("My name is {0}", s);
Console.WriteLine(withClosure.Method.IsStatic);
Console.WriteLine(withoutClosure.Method.IsStatic);

이 출력됩니다 false에 대한 withClosuretrue대한 withoutClosure.

람다 식을 사용할 때 컴파일러는 메서드를 포함하는 작은 클래스를 생성합니다.이 클래스는 다음과 같이 컴파일됩니다 (실제 구현은 약간 다를 수 있음).

private class <Main>b__0
{
    public int age;
    public void withClosure(string s)
    {
        Console.WriteLine("My name is {0} and I am {1} years old", s, age)
    }
}

private static class <Main>b__1
{
    public static void withoutClosure(string s)
    {
        Console.WriteLine("My name is {0}", s)
    }
}

public static void Main()
{
    var b__0 = new <Main>b__0();
    b__0.age = 25;
    Action<string> withClosure = b__0.withClosure;
    Action<string> withoutClosure = <Main>b__1.withoutClosure;
    Console.WriteLine(withClosure.Method.IsStatic);
    Console.WriteLine(withoutClosure.Method.IsStatic);
}

결과 Action<string>인스턴스가 실제로 이러한 생성 된 클래스의 메서드를 가리키는 것을 볼 수 있습니다 .


답변

“조치 방법”은 구현의 부작용으로 만 정적입니다. 캡처 된 변수가없는 익명 메서드의 경우입니다. 캡처 된 변수가 없기 때문에 메서드에는 일반적으로 로컬 변수에 대한 추가 수명 요구 사항이 없습니다. 다른 지역 변수를 참조했다면 수명이 다른 변수의 수명까지 연장됩니다 ( C # 5.0 사양의 섹션 L.1.7, 지역 변수 및 섹션 N.15.5.1, 캡처 된 외부 변수 참조).

C # 사양은 “익명 클래스”가 아닌 “표현식 트리”로 변환되는 익명 메서드에 대해서만 설명합니다. 식 트리는 예를 들어 Microsoft 컴파일러에서 추가 C # 클래스로 표현 될 수 있지만이 구현은 필요하지 않습니다 (C # 5.0 사양의 섹션 M.5.3에서 확인 됨). 따라서 익명 함수가 정적인지 여부는 정의되지 않습니다. 또한 섹션 K.6은 표현 트리의 세부 사항에 대해 많이 공개합니다.


답변

Roslyn에서 위임 캐싱 동작이 변경되었습니다. 이전에 언급했듯이 변수를 캡처하지 않은 람다 식은 static호출 사이트에서 메서드 로 컴파일되었습니다 . Roslyn은이 동작을 변경했습니다. 이제 변수를 캡처하거나 캡처하지 않는 모든 람다는 표시 클래스로 변환됩니다.

이 예에서 :

public class C
{
    public void M()
    {
        var x = 5;
        Action<int> action = y => Console.WriteLine(y);
    }
}

네이티브 컴파일러 출력 :

public class C
{
    [CompilerGenerated]
    private static Action<int> CS$<>9__CachedAnonymousMethodDelegate1;
    public void M()
    {
        if (C.CS$<>9__CachedAnonymousMethodDelegate1 == null)
        {
            C.CS$<>9__CachedAnonymousMethodDelegate1 = new Action<int>(C.<M>b__0);
        }
        Action<int> arg_1D_0 = C.CS$<>9__CachedAnonymousMethodDelegate1;
    }
    [CompilerGenerated]
    private static void <M>b__0(int y)
    {
        Console.WriteLine(y);
    }
}

Roslyn :

public class C
{
    [CompilerGenerated]
    private sealed class <>c__DisplayClass0
    {
        public static readonly C.<>c__DisplayClass0 CS$<>9__inst;
        public static Action<int> CS$<>9__CachedAnonymousMethodDelegate2;
        static <>c__DisplayClass0()
        {
            // Note: this type is marked as 'beforefieldinit'.
            C.<>c__DisplayClass0.CS$<>9__inst = new C.<>c__DisplayClass0();
        }
        internal void <M>b__1(int y)
        {
            Console.WriteLine(y);
        }
    }
    public void M()
    {
        Action<int> arg_22_0;
        if (arg_22_0 = C.
                       <>c__DisplayClass0.CS$<>9__CachedAnonymousMethodDelegate2 == null)
        {
            C.<>c__DisplayClass0.CS$<>9__CachedAnonymousMethodDelegate2 =
          new Action<int>(C.<>c__DisplayClass0.CS$<>9__inst.<M>b__1);
        }
    }
}

Roslyn의 위임 캐싱 동작 변경 이 이러한 변경이 이루어진 이유에 대해 설명합니다.


답변

C # 6부터는 항상 인스턴스 메서드가 기본값이되며 정적이 아니므 actionMethod.Method.IsStatic로 항상 false가됩니다.

여기 참조 : 캡처가없는 람다가 C # 5의 정적에서 C # 6의 인스턴스 메서드로 변경된 이유는 무엇입니까?

그리고 여기 : CSC와 Roslyn 컴파일러의 정적 람다 식 평가의 차이점은 무엇입니까?


답변

이 메서드에는 클로저가 없으며 정적 메서드 자체 (Console.WriteLine)도 참조하므로 정적 일 것으로 예상합니다. 이 메서드는 클로저에 대해 둘러싸는 익명 유형을 선언하지만이 경우에는 필요하지 않습니다.


답변