[c#] Roslyn이 코드를 컴파일하지 못했습니다.

프로젝트를 VS2013에서 VS2015로 마이그레이션 한 후 프로젝트가 더 이상 빌드되지 않습니다. 다음 LINQ 문에서 컴파일 오류가 발생합니다.

static void Main(string[] args)
{
    decimal a, b;
    IEnumerable<dynamic> array = new string[] { "10", "20", "30" };
    var result = (from v in array
                  where decimal.TryParse(v, out a) && decimal.TryParse("15", out b) && a <= b // Error here
                  orderby decimal.Parse(v)
                  select v).ToArray();
}

컴파일러는 오류를 반환합니다.

오류 CS0165 할당되지 않은 지역 변수 ‘b’사용

이 문제의 원인은 무엇입니까? 컴파일러 설정을 통해 수정할 수 있습니까?



답변

이 문제의 원인은 무엇입니까?

나에게 컴파일러 버그처럼 보입니다. 적어도 그렇습니다. 있지만 decimal.TryParse(v, out a)decimal.TryParse(v, out b)표현이 동적으로 평가, 내가 예상 컴파일러는 여전히 이해가가 도달하는 시간으로 a <= b, 모두 ab확실히 할당됩니다. 동적 타이핑에서 생각 해낼 수있는 이상한 점이 있더라도 a <= bTryParse호출을 모두 평가 한 후에 만 평가할 것으로 기대 합니다.

그러나 연산자와 변환을 통해 까다로워서 충분히 교활하다면 A && B && C평가 A하고 평가 C하지 않는 표현식을 갖는 것이 완전히 가능하다는 것이 밝혀졌습니다 B. Neal Gafter의 독창적 인 예 는 Roslyn 버그 보고서 를 참조하세요 .

이 작업을 수행하는 dynamic것은 훨씬 더 어렵습니다. 피연산자가 동적 일 때 관련된 의미는 설명하기가 더 어렵습니다. 왜냐하면 오버로드 해결을 수행하려면 어떤 유형이 관련되어 있는지 알아보기 위해 피연산자를 평가해야하는데, 이는 직관적이지 않을 수 있습니다. 그러나 Neal은 컴파일러 오류가 필요함을 보여주는 예제를 다시 제시했습니다. 이것은 버그가 아니라 버그 수정 입니다. 그것을 증명 한 Neal에게 엄청난 찬사를 보냅니다.

컴파일러 설정을 통해 수정할 수 있습니까?

아니요,하지만 오류를 방지하는 대안이 있습니다.

첫째, 동적이되는 것을 막을 수 있습니다. 문자열 만 사용할 것이라는 것을 알고 있다면 범위 변수 에 (예 🙂 유형을 사용 IEnumerable<string> 하거나 지정할 수 있습니다 . 그것이 제가 선호하는 옵션입니다.vstringfrom string v in array

당신이 경우 정말 그것을 동적를 계속해야, 단지 줄 b로 시작하는 값을 :

decimal a, b = 0m;

이것은 아무런 해를 끼치 지 않을 것입니다. 실제로 동적 평가가 미친 짓을하지 않는다는 것을 알고 있으므로 b사용하기 전에 값을 할당 하여 초기 값을 무의미하게 만듭니다.

또한 괄호 추가도 작동하는 것 같습니다.

where decimal.TryParse(v, out a) && (decimal.TryParse("15", out b) && a <= b)

이는 다양한 오버로드 해결이 트리거되는 지점을 변경하고 컴파일러를 행복하게 만듭니다.

하나 여전히 남아있는 문제 일 -와 명확한 과제에 대한 사양의 규칙 &&운영자 필요성은 때에만 적용한다는 것은 명확히하는 &&연산자는 두과의 “일반”구현에 사용되는 bool피연산자. 다음 ECMA 표준을 위해 이것이 수정되었는지 확인하려고 노력할 것입니다.


답변

이것은 Roslyn 컴파일러에서 버그 또는 적어도 회귀로 보입니다. 이를 추적하기 위해 다음 버그가 제출되었습니다.

https://github.com/dotnet/roslyn/issues/4509

그 동안 Jon의 탁월한 답변 에는 몇 가지 해결 방법이 있습니다.


답변

버그 리포트를 열심히 공부했기 때문에 직접 설명하려고합니다.


상상은 T에 암시 적 캐스트와 일부 사용자 정의 유형 bool간의 대체 false하고 true,로 시작 false. 컴파일러가 아는 한 dynamic첫 번째 인수 &&는 해당 유형으로 평가 될 수 있으므로 비관적이어야합니다.

그런 다음 코드가 컴파일되도록하면 다음과 같은 일이 발생할 수 있습니다.

  • 동적 바인더가 첫 번째를 평가할 때 &&다음을 수행합니다.
    • 첫 번째 인수 평가
    • 그것은 T암시 적으로 그것을 bool.
    • 아,이므로 false두 번째 인수를 평가할 필요가 없습니다.
    • &&평가 결과를 첫 번째 인수로 만듭니다. (아니요, false어떤 이유로 든 아닙니다 .)
  • 동적 바인더가 두 번째를 평가할 때 &&다음을 수행합니다.
    • 첫 번째 인수를 평가하십시오.
    • 그것은 T암시 적으로 그것을 bool.
    • 아, true그래서 두 번째 인수를 평가하십시오.
    • … 아 젠장, b할당되지 않았습니다.

요컨대, 변수가 “확실히 할당”되었는지 또는 “확실히 할당되지 않았는지”뿐만 아니라 ” false문 뒤에 명확하게 할당”또는 “확실히 할당되었는지”를 알려주는 특별한 “정확한 할당”규칙이 있습니다. true문 이후 할당 “.

이것들은 &&and ||(and !and ??and ?:)를 처리 할 때 컴파일러가 복잡한 부울 표현식의 특정 분기에 변수가 할당 될 수 있는지 여부를 검사 할 수 있도록 존재합니다.

그러나 표현식의 유형이 boolean으로 유지되는 동안에 만 작동 합니다 . 표현의 한 부분 인 경우 dynamic(또는 부울이 아닌 정적 유형) 우리는 더 이상 안정적으로 발현이라고 말할 수 있습니다 true또는 false– 다음 번에 우리가 캐스팅 bool소요되는 지점을 결정, 그것의 마음을 변경되었을 수 있습니다.


업데이트 : 이제이 문제해결 되고 문서화 되었습니다 .

동적 표현식에 대해 이전 컴파일러에 의해 구현 된 명확한 할당 규칙은 확실히 할당되지 않은 변수를 읽을 수있는 코드의 경우를 허용했습니다. 이에 대한 보고서는 https://github.com/dotnet/roslyn/issues/4509 를 참조 하십시오 .

이러한 가능성 때문에 컴파일러는 val에 초기 값이없는 경우이 프로그램이 컴파일되는 것을 허용하지 않아야합니다. 이전 버전의 컴파일러 (VS2015 이전)에서는 val에 초기 값이없는 경우에도이 프로그램을 컴파일 할 수있었습니다. Roslyn은 이제 초기화되지 않은 변수를 읽으려는이 시도를 진단합니다.


답변

이것은 버그가 아닙니다. 이 양식의 동적 표현이 이러한 out 변수를 할당되지 않은 상태로 둘 수있는 방법에 대한 예제는 https://github.com/dotnet/roslyn/issues/4509#issuecomment-130872713 을 참조 하십시오 .


답변