Почему System.Decimal игнорирует проверенный/непроверенный контекст

Я просто снова наткнулся на System.Decimal и попросил объяснить.

При выдаче значения типа System.Decimal для какого-либо другого типа (то есть System.Int32) ключевое слово checked и -checked компилятора -checked похоже, игнорируются.

Для демонстрации ситуации я создал следующий тест:

public class UnitTest
{
    [Fact]
    public void TestChecked()
    {
        int max = int.MaxValue;

        // Expected if compiled without the -checked compiler option or with -checked-
        Assert.Equal(int.MinValue, (int)(1L + max));

        // Unexpected
        // this would fail
        //Assert.Equal(int.MinValue, (int)(1M + max));
        // this succeeds
        Assert.Throws<OverflowException>(() => { int i = (int)(1M + max); });


        // Expected independent of the -checked compiler option as we explicitly set the context
        Assert.Equal(int.MinValue, unchecked((int)(1L + max)));

        // Unexpected
        // this would fail
        //Assert.Equal(int.MinValue, unchecked((int)(1M + max)));
        // this succeeds
        Assert.Throws<OverflowException>(() => { int i = unchecked((int)(1M + max)); });


        // Expected independent of the -checked compiler option as we explicitly set the context
        Assert.Throws<OverflowException>(() => { int i = checked((int)(1L + max)); });

        // Expected independent of the -checked compiler option as we explicitly set the context
        Assert.Throws<OverflowException>(() => { int i = checked((int)(1M + max)); });
    }
}

Все мои исследовательские подразделения теперь не привели к надлежащему объяснению этого явления или даже некоторой дезинформации, заявляющей, что она должна работать. Мои исследования уже включали спецификацию С#

Есть ли там кто-нибудь, кто может пролить свет на это?

Ответ 1

checked контекст относится к IL, испускаемому вашим кодом, - он в основном изменяет код операции, используемый для этих математических операций из непроверенной версии в проверенную версию. Он не может сделать это для decimal поскольку decimal не является примитивным и не имеет прямых кодов операций: все арифметические операции предварительно встроены в пользовательские операторы, точно так же, как если бы вы добавили свой собственный struct MyType и добавили операторов для Это. Итак: все будет зависеть от того, будут ли настраиваемые операторы, определяемые decimal выбирать и исключать OverflowException или нет, в этом коде. Что вы не контролируете и не можете влиять на вашу сборку.

Это decimal тип, который обеспечивает decimal преобразования <===> int. К тому времени, когда он вернется к вашему коду, где ключевое слово checked может иметь эффект, он уже либо генерирует int либо исключение.

К сожалению, поддержка пользовательского оператора С# не распространяется на возможность добавления отдельных проверочных/непроверенных реализаций операторов, к сожалению.

Ответ 2

Спецификация С# (раздел 12.7.14 Проверенные и непроверенные операторы) содержит список затронутых операторов и операторов. Операторов в вашем тесте нет в списке:

Следующие операции зависят от контекста проверки переполнения, установленного checked и unchecked операторами и операторами:

  • Предопределенные операторы ++ и -- (§12.7.10 и §12.8.6), когда операнд имеет интегральный или перечислимый тип.
  • Предопределенный - унарный оператор (§12.8.3), когда операнд имеет целочисленный тип.
  • Предопределенные +, -, * и / двоичные операторы (§12.9), когда оба операнда являются интегральными или перечисляемыми.
  • Явные числовые преобразования (§11.3.2) из одного интеграла или enumtype в другой интеграл или enumtype, или из float или double в интеграл или enumtype.