int에서 해제 / nullable 변환이있는 심각한 버그, 십진수 변환 허용
이 질문이 Stack Overflow에서 저에게 즉각적인 명성을 줄 것이라고 생각합니다.
다음 유형이 있다고 가정하십시오.
// represents a decimal number with at most two decimal places after the period
struct NumberFixedPoint2
{
decimal number;
// an integer has no fractional part; can convert to this type
public static implicit operator NumberFixedPoint2(int integer)
{
return new NumberFixedPoint2 { number = integer };
}
// this type is a decimal number; can convert to System.Decimal
public static implicit operator decimal(NumberFixedPoint2 nfp2)
{
return nfp2.number;
}
/* will add more nice members later */
}
정밀도를 잃지 않는 안전한 변환 만 허용되도록 작성되었습니다. 그러나이 코드를 시도하면 :
static void Main()
{
decimal bad = 2.718281828m;
NumberFixedPoint2 badNfp2 = (NumberFixedPoint2)bad;
Console.WriteLine(badNfp2);
}
나는이 컴파일을 놀라게하고, 실행할 때,하고 기록합니다2
. 여기서 int
(가치 2
) 에서 로의 변환 NumberFixedPoint2
이 중요합니다. ( 누군가 궁금해 할 경우를 대비 WriteLine
하여 과부하 System.Decimal
가 선호됩니다.)
왜 지구상에서에서 로의 전환 decimal
이 NumberFixedPoint2
허용됩니까? (덧붙여서 위의 코드 NumberFixedPoint2
에서 구조체에서 클래스로 변경하면 아무것도 변경되지 않습니다.)
C # 언어 사양 int
에서 사용자 지정 형식으로의 암시 적 변환 decimal
이 해당 사용자 지정 형식 에서 "직접"명시 적 변환의 존재를 "내포" 한다고 말하는지 알고 있습니까?
훨씬 더 나빠집니다. 대신 다음 코드를 시도하십시오.
static void Main()
{
decimal? moreBad = 7.3890560989m;
NumberFixedPoint2? moreBadNfp2 = (NumberFixedPoint2?)moreBad;
Console.WriteLine(moreBadNfp2.Value);
}
보시다시피 Nullable<>
여기에 전환이 있습니다. 그러나 오 예, 컴파일됩니다.
x86 "플랫폼" 에서 컴파일 할 때이 코드는 예측할 수없는 숫자 값을 작성합니다. 어느 것이 수시로 다릅니다. 예를 들어, 한 번은 2289956
. 자, 그것은 하나의 심각한 버그입니다!
x64 플랫폼 용으로 컴파일 할 때 위 코드는 공용 언어 런타임에서 잘못된 프로그램을 감지했습니다.라는System.InvalidProgramException
메시지와 함께 응용 프로그램과 충돌합니다 . InvalidProgramException
수업 문서에 따르면 :
일반적으로 이것은 프로그램을 생성 한 컴파일러의 버그를 나타냅니다.
누구든지 (예 : Eric Lippert 또는 C # 컴파일러에서 해제 된 변환 작업을 수행 한 사람) 이러한 버그의 원인을 알고 있습니까? 예를 들어, 코드에서 부딪히지 않는 충분한 조건은 무엇입니까? 유형 NumberFixedPoint2
은 실제로 우리가 실제 코드 (다른 사람의 돈과 물건을 관리하는)에있는 것이기 때문 입니다.
nullable 형식을 사용하는 두 번째 부분 은 현재 컴파일러에서 알려진이 버그 와 매우 유사합니다 . Connect 문제에 대한 답변 :
현재 Visual Studio의 다음 릴리스에서이 문제를 해결할 계획은 없지만 Roslyn에서 수정 사항을 조사 할 계획입니다.
따라서이 버그는 Visual Studio 및 컴파일러의 향후 릴리스에서 수정되기를 바랍니다.
시작하는 질문의 첫 번째 부분에 답장하고 있습니다. (두 번째 부분은 별도의 질문이어야하며 버그 일 가능성이 더 높습니다.)
에서 로의 명시 적 변환 만 있지만 해당 변환은 코드에서 암시 적으로 호출됩니다. 변환은이 IL에서 발생합니다.decimal
int
IL_0010: stloc.0
IL_0011: ldloc.0
IL_0012: call int32 [mscorlib]System.Decimal::op_Explicit(valuetype [mscorlib]System.Decimal)
IL_0017: call valuetype NumberFixedPoint2 NumberFixedPoint2::op_Implicit(int32)
I believe this is the correct behaviour according to the spec, even though it's surprising1. Let's work our way through section 6.4.5 of the C# 4 spec (User-Defined Explicit Conversions). I'm not going to copy out all the text, as it would be tedious - just what the relevant results are in our case. Likewise I'm not going to use subscripts, as they don't work well with code font here :)
- Determine the types
S0
andT0
:S0
isdecimal
, andT0
isNumberFixedPoint2
. - Find the set of types,
D
, from which used-defined conversion operators will be considered: just{ decimal, NumberFixedPoint2 }
- Find the set of applicable user-defined and lifted conversion operators,
U
.decimal
encompassesint
(section 6.4.3) because there's a standard implicit conversion fromint
todecimal
. So the explicit conversion operator is inU
, and is indeed the only member ofU
- Find the most specific source type,
Sx
, of the operators inU
- The operator doesn't convert from
S
(decimal
) so the first bullet is out - The operator doesn't convert from a type that encompasses
S
(decimal
encompassesint
, not the other way round) so the second bullet is out - That just leaves the third bullet, which talks about the "most encompassing type" - well, we've only got one type, so that's okay:
Sx
isint
.
- The operator doesn't convert from
- Find the most specific target type,
Tx
, of the operators inU
- The operator convers straight to
NumberFixedPoint2
soTx
isNumberFixedPoint2
.
- The operator convers straight to
- Find the most specific conversion operator:
U
contains exactly one operator, which does indeed convert fromSx
toTx
, so that's the most specific operator
- Finally, apply the conversion:
- If
S
is notSx
, then a standard explicit conversion fromS
toSx
is performed. (So that'sdecimal
toint
.) - The most specific user-defined conversion operator is invoked (your operator)
T
isTx
so there's no need for the conversion in the third bullet
- If
The line in bold is the bit which confirms that a standard explicit conversion really is feasible, when only an explicit conversion from a different type is actually specified.
1 Well I found it surprising, at least. I'm not aware of seeing this before.
'developer tip' 카테고리의 다른 글
ggplot2에서 범례를 이동하거나 배치하는 방법 (0) | 2020.12.09 |
---|---|
순수 가상 함수에는 인라인 정의가 없을 수 있습니다. (0) | 2020.12.09 |
분기없는 K- 평균 (또는 기타 최적화) (0) | 2020.12.09 |
React Router 인증 (0) | 2020.12.09 |
PDF 페이지가 컬러인지 흑백인지 어떻게 알 수 있습니까? (0) | 2020.12.09 |