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에서 발생합니다.decimalint
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
S0andT0:S0isdecimal, andT0isNumberFixedPoint2. - 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.decimalencompassesint(section 6.4.3) because there's a standard implicit conversion frominttodecimal. 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(decimalencompassesint, 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:
Sxisint.
- The operator doesn't convert from
- Find the most specific target type,
Tx, of the operators inU- The operator convers straight to
NumberFixedPoint2soTxisNumberFixedPoint2.
- The operator convers straight to
- Find the most specific conversion operator:
Ucontains exactly one operator, which does indeed convert fromSxtoTx, so that's the most specific operator
- Finally, apply the conversion:
- If
Sis notSx, then a standard explicit conversion fromStoSxis performed. (So that'sdecimaltoint.) - The most specific user-defined conversion operator is invoked (your operator)
TisTxso 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 |