c# 複数 System.Decimal がチェック/オフチェックされていないコンテキストを無視する理由




チェックボックス 未選択 値 (3)

checkedコンテキストは、コードから生成されたILに関連します。基本的には、数学演算に使用されるオペコードをチェックされていないバージョンからチェックされたバージョンに変更します。 decimal はプリミティブではなく、直接オペコードもないため、 decimal はできません 。すべての算術演算は、独自のstruct MyTypeを追加して演算子を追加した場合とまったく同じように、カスタム演算子で事前に構築されますそれ。 つまり、 decimal定義されたカスタム演算子が、そのコードで OverflowExceptionを検出してスローするかどうかによって異なります 。 あなたはコントロールできず、ビルドに影響を与えることはできません。

decimal <===> int変換を提供するdecimal型です。 checkedキーワードが効果を持つ可能性があるコードに戻ってくるとすでにintか例外がスローされています。

C#カスタム演算子のサポートは、悲しいことに、別々のチェック/チェックされていない演算子の実装を追加できるようにまで拡張されていません。

私はもう一度System.Decimal不思議に遭遇し、説明を求めました。

System.Decimal型の値を他の型( System.Int32 )にキャストすると、 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)); });
    }
}

すべての私の研究ユニットは今、この現象についての適切な説明やそれがうまくいくと主張するいくつかの誤った情報につながっていませんでした。 私の研究には既にC#の仕様が含まれています

これについて何か光を当てることができる誰かがそこにいますか?


CLRは、 add (加算)、 sub (減算)、 mul (乗算)、 div (除算)などの簡単な算術演算のIL命令を提供します。

たとえば、2つの値を加算するadd命令を実行できます。 add命令はオーバーフローチェックを実行しませんが、 add.ovfという命令もあります。これも2つの値を加算しますが、オーバーフローが発生した場合はOverflowExceptionをスローします。

したがって、 checked演算子、文またはコンパイラスイッチを使用しているときは、 add命令( add.ovf )の"オーバーフローチェック"バージョンを使用します。

これは「プリミティブ型」に対してのみ機能することを忘れないでください。

しかしdecimal付きでは事はほとんど変わりません。 decimal型はCLRではプリミティブ型とは見なされません(ただし、C#やVisual Basicなどのプログラミング言語では同じですが).CLRにはdecimal進値の操作方法を示すIL命令がありません。 .NET Framework SDK DocumantationまたはReferenceSourcesのソースコードで decimal型を検索すると、 +, -, *, /, etc演算子オーバーロードメソッドがAdd, Subtract, Multiply, Divide, etc..メソッドがあることに気付くでしょうAdd, Subtract, Multiply, Divide, etc.. +, -, *, /, etc

decimalを使用するコードをコンパイルすると、実際の操作を実行するためのdecimalを呼び出すコードがコンパイラによって生成されます。 また、 decimal値を操作するためのIL命令がないため、 checked/unchecked演算子/ステートメント/コンパイラスイッチは効果がありません。 操作を安全に実行できない場合、小数値を使用した操作は常にOverflowExceptionスローします。


C#の仕様(セクション12.7.14チェックされたオペレータとチェックされていないオペレータ)には、影響を受けるオペレータとステートメントのリストが含まれています。 あなたのテストの演算子はリストにありません:

次の操作は、 checkedされた演算子とcheckedされていunchecked演算子とステートメントによって設定されたオーバーフローチェックコンテキストの影響を受けchecked

  • オペランドが整数型または列挙型の場合に定義された++および--演算子(§12.7.10および§12.8.6)。
  • オペランドが整数型の事前定義された単項演算子(§12.8.3)。
  • 両方のオペランドが整数型または列挙型である場合に、あらかじめ定義された+-*および/ 2進演算子(12.9)。
  • 1つの整数型または列挙型から別の整数型または列挙型への明示的な数値変換(11.3.2)、またはfloatまたはdoubleから整数型またはenumtypeへの明示的な数値変換(11.3.2)。




.net