단락 연산자를 해 || &&는 nullable 부울에 대해 존재합니까? RuntimeBinder는 때때로 그렇게 생각합니다.


84

조건부 논리 연산자 ||&&단락 논리 연산자라고도 하는 C # 언어 사양을 읽었습니다 . 나에게 nullable booleans, 즉 피연산자 유형 Nullable<bool>(또한 작성 됨 bool?)에 대해 존재하는지 명확하지 않은 것처럼 보였으 므로 비 동적 유형으로 시도했습니다.

bool a = true;
bool? b = null;
bool? xxxx = b || a;  // compile-time error, || can't be applied to these types

그것은 질문을 해결하는 것처럼 보였습니다 (사양을 명확하게 이해할 수는 없지만 Visual C # 컴파일러의 구현이 정확하다고 가정하면 이제 알았습니다).

하지만 dynamic바인딩도 해보고 싶었습니다 . 그래서 대신 이것을 시도했습니다.

static class Program
{
  static dynamic A
  {
    get
    {
      Console.WriteLine("'A' evaluated");
      return true;
    }
  }
  static dynamic B
  {
    get
    {
      Console.WriteLine("'B' evaluated");
      return null;
    }
  }

  static void Main()
  {
    dynamic x = A | B;
    Console.WriteLine((object)x);
    dynamic y = A & B;
    Console.WriteLine((object)y);

    dynamic xx = A || B;
    Console.WriteLine((object)xx);
    dynamic yy = A && B;
    Console.WriteLine((object)yy);
  }
}

놀라운 결과는 예외없이 실행된다는 것입니다.

음, x그리고 y놀라운 일이 아니다되어, 그 선언은 두 속성이 검색되는 이어질하고, 결과 값은 예상대로되어 x있습니다 truey입니다 null.

그러나에 대한 평가 xxA || B인해 바인딩 시간 예외가 발생 A하지 않고 B. 왜 이런 일이 발생합니까? 당신이 말할 수있는, 우리는 변경 될 수 있습니다 B처럼, 미친 개체를 반환 게터를 "Hello world"하고, xx여전히로 평가 것 true바인딩 문제없이 ...

평가 A && B(for yy)도 바인딩 시간 오류가 발생하지 않습니다. 물론 여기서 두 속성이 모두 검색됩니다. 런타임 바인더에서 이것이 허용되는 이유는 무엇입니까? 에서 반환 된 객체 B가 "불량"객체 (예 :)로 변경 string되면 바인딩 예외가 발생합니다.

이것이 올바른 행동입니까? (사양에서 어떻게 추론 할 수 있습니까?)

당신이하려고하면 B첫 번째 피연산자로, 모두 B || AB && A(런타임 바인더 예외 제공 B | AB & A모든 비 단락 사업자와 정상으로 잘 작동을 |하고 &).

(Visual Studio 2013의 C # 컴파일러 및 런타임 버전 .NET 4.5.2로 시도했습니다.)


4
Nullable<Boolean>관련된 인스턴스가 전혀 없으며 박스형 부울 만 처리됩니다 . dynamic테스트 bool?는 관련이 없습니다. (물론, 이것은 전체 응답, 하나의 세균이 아니다.)
제론 Mostert

3
A || B당신이 평가하지 않으려는 점에서 의미의 일정 금액을하게 B하지 않는 A거짓이 아니다있는. 그래서 당신은 표현의 유형을 결코 알지 못합니다. A && B버전은 더욱 놀라운 일이다 - 나는 사양에서 찾을 수 있는지 확인할 수 있습니다.
존 소총

2
@JeroenMostert : 컴파일러의 종류가 있다면 것을 결정했다하지 않는 자, A이다 bool와의 값 B입니다 null, 다음 bool && bool?운영자가 관련 될 수 있습니다.
존 소총

4
흥미롭게도 이것이 컴파일러 또는 사양 버그를 노출시킨 것 같습니다. C # 5.0 사양 &&&대신에있는 것처럼 해결하는 방법 에 대해 설명 하고 특히 두 피연산자가 모두있는 경우를 포함 bool?하지만 참조하는 다음 섹션에서는 nullable 경우를 처리하지 않습니다. 그것에 대해 더 자세히 설명하는 일종의 대답을 추가 할 수는 있지만 완전히 설명하지는 않습니다.
존 소총

14
나는 ... 그냥 문제인지 내가 그것을 읽고 있어요 방법에 볼 수있는 사양 문제에 대한 MADS를 이메일로 전송 한
존 소총을

답변:


67

우선, non-dynamic nullable-bool 케이스에 대한 사양이 명확하지 않다는 점을 지적 해 주셔서 감사합니다. 향후 버전에서 수정하겠습니다. 컴파일러의 동작은 의도 된 동작입니다. &&||널 (NULL) bools에 대한 작업에되어 있지 않습니다.

그러나 동적 바인더는이 제한을 구현하지 않는 것 같습니다. 대신 구성 요소 작업을 별도로 바인딩합니다 : &/ |?:. 따라서 첫 번째 피연산자가 trueor false(부울 값이므로의 첫 번째 피연산자로 허용됨) 일 경우 혼란 스러울 수 ?:있지만 null첫 번째 피연산자로 제공 하면 (예 : B && A위의 예에서 시도한 경우 ) 다음을 수행합니다. 런타임 바인딩 예외가 발생합니다.

당신이 그것에 대해 생각한다면 우리는 동적 구현 이유를 알 수 &&||대신에 하나의 큰 동적 작업의 방법 : 동적 작업은 런타임에 바인딩 된 피연산자를 평가 한 후 , 바인딩이 결과의 실행 유형을 기반으로 할 수 있도록하는 것이 그 평가의. 그러나 그러한 열성적인 평가는 오퍼레이터 단락의 목적을 무너 뜨립니다! 그래서 그 대신, 동적에 대해 생성 된 코드 &&||같이 진행하고 조각으로 나누기 평가 업 다음과 같습니다 :

  • 왼쪽 피연산자를 평가합니다 (결과를 호출합시다 x).
  • bool암시 적 변환을 통해 변환하거나 true또는 false연산자 (불가능하면 실패) 로 바꾸십시오.
  • 작업 x조건으로 사용?:
  • 실제 분기에서 x결과로 사용
  • 거짓 분기에서 이제 두 번째 피연산자를 평가합니다 (결과를 호출합시다 y).
  • 바인드 시도 &또는 |의 실행시의 형태에 따라 연산자 xy(없는 경우 실패)
  • 선택한 연산자 적용

이것은 피연산자의 특정 "불법"조합을 허용하는 동작입니다. ?:연산자는 첫 번째 피연산자를 nullable아닌 부울로 성공적으로 처리 하고 &or |연산자는이를 nullable 부울 로 성공적으로 처리 하며 두 피연산자는 일치하는지 확인하기 위해 조정하지 않습니다. .

그래서 그것은 동적이 아니고 && 그리고 || nullables에서 작동합니다. 정적 인 경우에 비해 약간 관대 한 방식으로 구현 된 것입니다. 이것은 아마도 버그로 간주되어야 할 것이지만, 그것이 주요 변경이 될 것이기 때문에 우리는 그것을 고치지 않을 것입니다. 또한 누구도 행동을 강화하는 데 거의 도움이되지 않습니다.

바라건대 이것은 무슨 일이 일어나고 왜 일어나는지 설명합니다! 이것은 흥미로운 영역이며, 우리가 동적을 구현할 때 내린 결정의 결과에 종종 당혹스러워합니다. 이 질문은 맛 있었어요-알려 주셔서 감사합니다!

Mads


동적 바인딩을 사용하면 단락이 발생하는 경우 두 번째 피연산자의 유형을 알 수 없기 때문에 이러한 단락 연산자가 특별하다는 것을 알 수 있습니다. 사양에서 언급해야할까요? 물론, 안에있는 모든 dynamic것이 박스형 이기 때문에 bool?which HasValue와 "simple" 의 차이를 구분할 수 없습니다 bool.
Jeppe Stig Nielsen

6

이것이 올바른 행동입니까?

네, 확실합니다.

사양에서 어떻게 추론 할 수 있습니까?

C # 사양 버전 5.0의 섹션 7.12에는 조건부 연산자 &&||동적 바인딩 과 관련된 정보에 대한 정보 가 있습니다. 관련 섹션 :

조건부 논리 연산자의 피연산자에 동적 컴파일 타임 유형이 있으면식이 동적으로 바인딩됩니다 (§7.2.2). 이 경우 표현식의 컴파일 타임 유형은 동적이며 컴파일 타임 유형이 dynamic 인 피연산자의 런타임 유형을 사용하여 런타임에 아래에 설명해결이 수행됩니다 .

이것이 귀하의 질문에 답하는 핵심 포인트라고 생각합니다. 런타임에 발생하는 해결 방법은 무엇입니까? 섹션 7.12.2, 사용자 정의 조건부 논리 연산자는 다음을 설명합니다.

  • x && y 연산은 T.false (x)? x : T. & (x, y), 여기서 T.false (x)는 T에서 선언 된 false 연산자의 호출이고 T. & (x, y)는 선택된 연산자 &의 호출입니다.
  • 작업 x || y는 T.true (x)? x : T. | (x, y), 여기서 T.true (x)는 T에서 선언 된 true 연산자의 호출이고 T. | (x, y)는 선택한 연산자 |의 호출입니다.

두 경우 모두 첫 번째 피연산자 x는 false또는 true연산자를 사용하여 부울로 변환됩니다 . 그런 다음 적절한 논리 연산자가 호출됩니다. 이를 염두에두고 나머지 질문에 답할 수있는 충분한 정보가 있습니다.

하지만 A의 xx에 대한 평가 || B는 바인딩 시간 예외를 일으키지 않고 B가 아닌 속성 A 만 읽었습니다. 왜 이런 일이 발생합니까?

를 들어 ||운영자, 우리는 다음과 알고있다 true(A) ? A : |(A, B). 우리는 단락하므로 바인딩 시간 예외가 발생하지 않습니다. 해도 A이었다 false, 우리는 것입니다 여전히 때문에 지정된 해결 단계의 예외를 실행시 바인딩을 얻을 수 없습니다. 경우 A이며 false, 우리는 그 다음 어떻게 |성공적으로 제 7.11.4 당, 널 값을 처리 할 수있는 연산자를.

A && B (yy의 경우)를 평가해도 바인딩 시간 오류가 발생하지 않습니다. 물론 여기서 두 속성이 모두 검색됩니다. 런타임 바인더에서 이것이 허용되는 이유는 무엇입니까? B에서 반환 된 객체가 문자열과 같은 "잘못된"객체로 변경되면 바인딩 예외가 발생합니다.

비슷한 이유로 이것도 작동합니다. &&로 평가됩니다 false(x) ? x : &(x, y). A으로 성공적으로 변환 될 수 있으므로 bool문제가 없습니다. B이 null 이기 때문에 &연산자는 a를 받는 연산자 bool에서 bool?매개 변수를 받는 연산자로 해제 되므로 (7.3.7 단원) 런타임 예외가 없습니다.

두 조건부 연산자 모두에 B대해 bool (또는 null 동적)이 아닌 경우에는 bool 및 non-bool을 매개 변수로 사용하는 오버로드를 찾을 수 없기 때문에 런타임 바인딩이 실패합니다. 그러나 A이는 연산자에 대한 첫 번째 조건 ( truefor ||, falsefor &&) 을 충족하지 못하는 경우에만 발생합니다 . 이것이 발생하는 이유는 동적 바인딩이 매우 게으 르기 때문입니다. A거짓이 아닌 한 논리 연산자를 바인딩하려고 시도하지 않으며 논리 연산자 를 평가하기 위해 해당 경로로 이동해야합니다. 일단 A오퍼레이터의 첫 번째 조건을 만족하지 못하는, 상기 결합 제외 실패한다.

B를 첫 번째 피연산자로 시도하면 둘 다 B || A 및 B && A는 런타임 바인더 예외를 제공합니다.

바라건대, 지금 쯤이면 왜 이런 일이 발생하는지 이미 알고 있습니다 (또는 설명을 잘못했습니다). 이 조건부 연산자를 해결하는 첫 번째 단계 는 논리 연산을 처리하기 전에 첫 번째 피연산자를 취하고 B부울 변환 연산자 ( false(B)또는 true(B)) 중 하나를 사용하는 것입니다 . 물론 B, being nulltrue또는 로 변환 될 수 없으므로 false런타임 바인딩 예외가 발생합니다.


놀랄 그와 dynamic바인딩은 인스턴스의 실제 유형이 아닌 컴파일 시간 유형 (첫 번째 따옴표)를 사용하여 런타임에 발생합니다. 여기에 유형이 operator true및을 오버로드하지 않으므로 두 번째 인용문은 관련이 없습니다 operator false. explicit operator반환은 bool보다 뭔가 다른 operator truefalse. 가능하게하는 모든 방법으로 스펙을 알아보기 힘들 A && B도 허용하지 않고, (내 예제에서) 와 정적으로 입력되는 널 (NULL) 논리 값, 즉 및 컴파일 시간에 바인딩. 그러나 그것은 허용되지 않습니다. a && babbool? abool? b
Jeppe Stig Nielsen

-1

Nullable 유형은 조건부 논리 연산자를 정의하지 않습니다 || 및 &&. 다음 코드를 제안합니다.

bool a = true;
bool? b = null;

bool? xxxxOR = (b.HasValue == true) ? (b.Value || a) : a;
bool? xxxxAND = (b.HasValue == true) ? (b.Value && a) : false;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.