조건부 논리 연산자 ||
및 &&
단락 논리 연산자라고도 하는 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
있습니다 true
및 y
입니다 null
.
그러나에 대한 평가 xx
로 A || B
인해 바인딩 시간 예외가 발생 A
하지 않고 B
. 왜 이런 일이 발생합니까? 당신이 말할 수있는, 우리는 변경 될 수 있습니다 B
처럼, 미친 개체를 반환 게터를 "Hello world"
하고, xx
여전히로 평가 것 true
바인딩 문제없이 …
평가 A && B
(for yy
)도 바인딩 시간 오류가 발생하지 않습니다. 물론 여기서 두 속성이 모두 검색됩니다. 런타임 바인더에서 이것이 허용되는 이유는 무엇입니까? 에서 반환 된 객체 B
가 “불량”객체 (예 :)로 변경 string
되면 바인딩 예외가 발생합니다.
이것이 올바른 행동입니까? (사양에서 어떻게 추론 할 수 있습니까?)
당신이하려고하면 B
첫 번째 피연산자로, 모두 B || A
와 B && A
(런타임 바인더 예외 제공 B | A
및 B & A
모든 비 단락 사업자와 정상으로 잘 작동을 |
하고 &
).
(Visual Studio 2013의 C # 컴파일러 및 런타임 버전 .NET 4.5.2로 시도했습니다.)
답변
우선, non-dynamic nullable-bool 케이스에 대한 사양이 명확하지 않다는 점을 지적 해 주셔서 감사합니다. 향후 버전에서 수정하겠습니다. 컴파일러의 동작은 의도 된 동작입니다. &&
및 ||
널 (NULL) bools에 대한 작업에되어 있지 않습니다.
그러나 동적 바인더는이 제한을 구현하지 않는 것 같습니다. 대신 구성 요소 작업을 별도로 바인딩합니다 : &
/ |
및 ?:
. 따라서 첫 번째 피연산자가 true
or false
(부울 값이므로의 첫 번째 피연산자로 허용됨) 일 경우 혼란 스러울 수 ?:
있지만 null
첫 번째 피연산자로 제공 하면 (예 : B && A
위의 예에서 시도한 경우 ) 다음을 수행합니다. 런타임 바인딩 예외가 발생합니다.
당신이 그것에 대해 생각한다면 우리는 동적 구현 이유를 알 수 &&
및 ||
대신에 하나의 큰 동적 작업의 방법 : 동적 작업은 런타임에 바인딩 된 피연산자를 평가 한 후 , 바인딩이 결과의 실행 유형을 기반으로 할 수 있도록하는 것이 그 평가의. 그러나 그러한 열성적인 평가는 오퍼레이터 단락의 목적을 무너 뜨립니다! 그래서 그 대신, 동적에 대해 생성 된 코드 &&
와 ||
같이 진행하고 조각으로 나누기 평가 업 다음과 같습니다 :
- 왼쪽 피연산자를 평가합니다 (결과를 호출합시다
x
). bool
암시 적 변환을 통해 변환하거나true
또는false
연산자 (불가능하면 실패) 로 바꾸십시오.- 작업
x
조건으로 사용?:
- 실제 분기에서
x
결과로 사용 - 거짓 분기에서 이제 두 번째 피연산자를 평가합니다 (결과를 호출합시다
y
). - 바인드 시도
&
또는|
의 실행시의 형태에 따라 연산자x
와y
(없는 경우 실패) - 선택한 연산자 적용
이것은 피연산자의 특정 “불법”조합을 허용하는 동작입니다. ?:
연산자는 첫 번째 피연산자를 nullable 이 아닌 부울로 성공적으로 처리 하고 &
or |
연산자는이를 nullable 부울 로 성공적으로 처리 하며 두 피연산자는 일치하는지 확인하기 위해 조정하지 않습니다. .
그래서 그것은 동적이 아니고 && 그리고 || nullables에서 작동합니다. 정적 인 경우에 비해 약간 관대 한 방식으로 구현 된 것입니다. 이것은 아마도 버그로 간주되어야 할 것이지만, 그것이 주요 변경이 될 것이기 때문에 우리는 그것을 고치지 않을 것입니다. 또한 누구도 행동을 강화하는 데 거의 도움이되지 않습니다.
바라건대 이것은 무슨 일이 일어나고 왜 일어나는지 설명합니다! 이것은 흥미로운 영역이며, 우리가 동적을 구현할 때 내린 결정의 결과에 종종 당혹스러워합니다. 이 질문은 맛 있었어요-알려 주셔서 감사합니다!
Mads
답변
이것이 올바른 행동입니까?
네, 확실합니다.
사양에서 어떻게 추론 할 수 있습니까?
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
이는 연산자에 대한 첫 번째 조건 ( true
for ||
, false
for &&
) 을 충족하지 못하는 경우에만 발생합니다 . 이것이 발생하는 이유는 동적 바인딩이 매우 게으 르기 때문입니다. A
거짓이 아닌 한 논리 연산자를 바인딩하려고 시도하지 않으며 논리 연산자 를 평가하기 위해 해당 경로로 이동해야합니다. 일단 A
오퍼레이터의 첫 번째 조건을 만족하지 못하는, 상기 결합 제외 실패한다.
B를 첫 번째 피연산자로 시도하면 둘 다 B || A 및 B && A는 런타임 바인더 예외를 제공합니다.
바라건대, 지금 쯤이면 왜 이런 일이 발생하는지 이미 알고 있습니다 (또는 설명을 잘못했습니다). 이 조건부 연산자를 해결하는 첫 번째 단계 는 논리 연산을 처리하기 전에 첫 번째 피연산자를 취하고 B
부울 변환 연산자 ( false(B)
또는 true(B)
) 중 하나를 사용하는 것입니다 . 물론 B
, being null
은 true
또는 로 변환 될 수 없으므로 false
런타임 바인딩 예외가 발생합니다.
답변
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;