2017-09-15 14 views
1

IQueryable의 구현 작업을하고 있습니다. 하지만 심오한 부분에 뛰어 들기 전에 평가할 표현의 나무가 어떻게 생겼는지 완전히 이해하고 있는지 확인하고 싶습니다. 특히 LINQ 쿼리 구문이 컴파일 프로세스 중 메서드 구문으로 변환되는 방법에 대해 궁금합니다.LINQ는 명명 충돌을 어떻게 해결합니까?

저는 LINQPad를 사용하여 컴파일러에서 생성 된 메서드를 봅니다. 중첩 된 반복에서 상위 레벨 반복의 상태를 저장하기 위해 임시 변수 이름이 생성되었음을 확인했습니다. 물론

EventQueue 
    .SelectMany(
    Event => Event.Acknowledgements, 
    (Event, Ack) => 
     new 
     { 
     Event = Event, 
     Ack = Ack 
     } 
) 
    .Where(temp0 => (temp0.Ack.User == User.Name)) 
    .Select(temp0 => temp0.Event) 

내 첫 번째 본능이 휴식과 무슨 일이 있었는지 볼을 시도하는 것입니다

from Event in EventQueue 
from Ack in Event.Acknowledgements 
where Ack.User == User.Name 
select Event 

이 동일합니다 예를 들면 다음과 같습니다. 그래서 다음 쿼리를 작성했습니다 :

from Event in EventQueue 
from Ack in Event.Acknowledgements 
let temp0 = Ack.User 
where Ack.User == temp0 
select Event 

이것은 거의 "WHERE 1 = 1"이며 모든 이벤트를 반환합니다. 이 LINQPad 컴파일러에서 이러한 방법 체인을 당겨하지 않는 결론에 저를 주도하고있다

EventQueue 
    .SelectMany(
    Event => Event.Acknowledgements, 
    (Event, Ack) => 
     new 
     { 
     Event = Event, 
     Ack = Ack 
     } 
) 
    .Select(
    temp0 => 
     new 
     { 
     temp0 = temp0, 
     temp0 = temp0.Ack.User // Anonymous object with identically-named properties 
     } 
) 
    .Where(temp1 => (temp1.temp0.Ack.User == temp1.temp0)) 
    .Select(temp1 => temp1.temp0.Event) 

을하기 때문에, 그러나, 나는 주어진있어 메소드 체인 컴파일하지 않을 것 때문에이 작업을 어떻게 이해하지 않는다 쿼리는이 메서드 체인이 분명히 작동하지 않는 동안 작동합니다. LINQPad는 자체적으로 메서드 체인을 생성 할 가능성이 큽니다.

어떻게 C# 컴파일러 (이 경우 Roslyn)가 생성 된 코드와 명명 충돌을 처리합니까?

답변

6

이로 인해 LINQPad가 컴파일러에서 이러한 메서드 체인을 가져 오지 못하게되었습니다.

컴파일러에서 얻은 결과에서 가져온 것이기 때문에 정확합니다.

C# 코드를 컴파일하고 컴파일 한 다음 도구를 사용하여 해당 코드에 대한 뷰를 다시 제공했습니다. 이 개 장소가 있다고하기에,

EventQueue.SelectMany(
    Event => Event.Acknowledgements, 
    (Event, Ack) => { Event = Event, Ack = Ack} 
) 
    .Select(x => new { x = x, temp0 = x.Ack.User}) 
    .Where(y => (y.x.Ack.User == y.temp0)) 
    .Select(y => y.x.Event) 

이제 우리가 수동으로 C#에서 확장 메서드 호출에 쿼리 구문 C# 코드에서 번역한다면

, 우리는 가능성 같은 것을 가지고 올 것 어디에서 람다 인수의 이름을 찾아야 만했는지. 나는 여기에 xy으로 갔다. foobar 또는 theUnbearableLightnessOfBeingforgettingWhatYouCameForTheMomentYouSetFootInAShop 또는 무엇이든 사용할 수 있습니다.

C# 컴파일러의 출력을 C#으로 되돌리려 고 시도했을 때 사용한 도구가 temp0으로 시작하고 temp1 등으로 시작하는 명명 체계를 선택했을 때 비슷한 작업을 수행했습니다. temp0이라고 명시 적으로 지정된 것이 있었으므로이 사례를 설명하지 않았으므로 불행합니다. 실제로 temp0은 나쁜 이름입니다. 제가이 도구를 만드는 데 관여했다면 그 문제를 해결하는 것이 최우선 순위가되지 않을 것입니다.

어떻게 C# 컴파일러 (이 경우 Roslyn)가 생성 된 코드와 이름 충돌을 처리합니까?

두 가지 방법에 필요하지

  1. . 많은 C# 구문에는 생성 된 IL에 전혀 이름이 없습니다. 이것의 IL이 될 것입니다

    public int DoSum() 
    { 
        int x = 2; 
        int y = 3; 
        int z = x * y + 2; 
        return z - 2; 
    } 
    

    뭔가 같은 :

고려

거기에는 x, y 또는 z이 없음을
ldc.i4.2  
ldc.i4.3  
mul   
ldc.i4.2  
add   
ldc.i4.2  
sub   
ret 

참고. 일리노이에서 C#으로가는 뭔가가 거기에 이름을 만들어야 할 것입니다.

  1. 유효하지 않은 이름 사용 C#.

IL에서 이름이 지정되어 있고 그 이름이 원본에 없다면 C# 컴파일러는 .NET 식별자로 유효하지만 유효하지 않은 이름을 사용합니다 C# 식별자로. 허용 된 식별자에 대한 .NET 규칙은 C# 규칙보다 훨씬 느슨합니다.

따라서 <>h__TransparentIdentifier0, <>h__TransparentIdentifier1과 같은 매개 변수 이름은 C# 변수 이름으로 사용할 수 없지만 일반적으로 .NET 규칙에 의해 완벽하게 괜찮으며 자체 생성 된 이름 만 추적하면됩니다. 이러한 이름은 C#에서는 유효하지 않으므로 작성자가 C#에 입력 한 내용에는 충돌이 없습니다. (이렇게하면 yield을 생성하면 생성 된 열거 형이 생성 한 클래스와 충돌하지 않습니다.

다시 IL에서 C#으로가는 것은 유효한 C#을 만들기 위해 여기에 새로운 이름을 만들어야 할 것입니다.

temp0을 사용할 때 도구에서 문제가 있다고 불평 할 수도 있지만 사용자 정의 이름과의 충돌을 검사하는 것이 좋을 수도 있지만 "나에게이 일을 해줘서 좋지 않습니다. 컴파일러가 수행 한 작업에서 C#으로 되돌아갑니다. " 컴파일러가 실제로 원하는 것을 원한다면 IL 탭을 사용하십시오.