Я думаю, что этот вопрос принесет мне мгновенную славу здесь в 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
, которая принимает значение a System.Decimal
, является предпочтительной, если кто-то задается вопросом.)
Почему на Земле разрешено преобразование с decimal
в NumberFixedPoint2
? (Кстати, в приведенном выше коде, если NumberFixedPoint2
изменяется от структуры к классу, ничего не меняется.)
Знаете ли вы, что спецификация языка С# говорит, что неявное преобразование из int
в пользовательский тип подразумевает существование "прямого" явного преобразования из decimal
в этот пользовательский тип?
Это становится намного хуже. Вместо этого попробуйте использовать этот код:
static void Main()
{
decimal? moreBad = 7.3890560989m;
NumberFixedPoint2? moreBadNfp2 = (NumberFixedPoint2?)moreBad;
Console.WriteLine(moreBadNfp2.Value);
}
Как вы видите, у нас есть (поднятые) Nullable<>
преобразования. Но о да, это компилируется.
При компиляции в платформе x86 "этот код выдает непредсказуемое числовое значение. Какой из них меняется время от времени. Например, однажды я получил 2289956
. Теперь эта серьезная ошибка!
При компиляции для платформы x64 приведенный выше код сбой приложения с System.InvalidProgramException
с сообщением Common Language Runtime обнаружил недействительную программу.. Согласно документации класс InvalidProgramException
:
Обычно это указывает на ошибку в компиляторе, который сгенерировал программу.
Кто-нибудь (например, Эрик Липперт или кто-то, кто работал с отмененными преобразованиями в компиляторе С#) знает причину этих ошибок? Как, что является достаточным условием, что мы не сталкиваемся с ними в нашем коде? Поскольку тип NumberFixedPoint2
на самом деле является тем, что мы имеем в реальном коде (управляем другими людьми деньгами и т.д.).