developer tip

int에서 해제 / nullable 변환이있는 심각한 버그, 십진수 변환 허용

optionbox 2020. 12. 9. 08:05
반응형

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가 선호됩니다.)

왜 지구상에서에서 로의 전환 decimalNumberFixedPoint2허용됩니까? (덧붙여서 위의 코드 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 S0 and T0: S0 is decimal, and T0 is NumberFixedPoint2.
  • 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 encompasses int (section 6.4.3) because there's a standard implicit conversion from int to decimal. So the explicit conversion operator is in U, and is indeed the only member of U
  • Find the most specific source type, Sx, of the operators in U
    • 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 encompasses int, 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 is int.
  • Find the most specific target type, Tx, of the operators in U
    • The operator convers straight to NumberFixedPoint2 so Tx is NumberFixedPoint2.
  • Find the most specific conversion operator:
    • U contains exactly one operator, which does indeed convert from Sx to Tx, so that's the most specific operator
  • Finally, apply the conversion:
    • If S is not Sx, then a standard explicit conversion from S to Sx is performed. (So that's decimal to int.)
    • The most specific user-defined conversion operator is invoked (your operator)
    • T is Tx so there's no need for the conversion in the third bullet

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.

참고URL : https://stackoverflow.com/questions/18342943/serious-bugs-with-lifted-nullable-conversions-from-int-allowing-conversion-from

반응형