簡體   English   中英

為什么System.Decimal忽略已選中/未選中的上下文

[英]Why does System.Decimal ignore checked/unchecked context

我只是偶然發現了一個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#規范

有沒有人可以對此有所了解?

checked上下文與從代碼中發出的IL相關 - 它基本上將用於那些數學運算的操作碼從未檢查的版本更改為檢查的版本。 它不能用於decimal因為decimal 不是一個原語 ,並且沒有直接的操作碼:所有的算術運算都是在自定義運算符中預先構建的,就像你添加自己的struct MyType並添加了運算符一樣。它。 所以:這將取決於decimal定義的自定義運算符是否選擇在該代碼中檢測並拋出OverflowException 你無法控制,也不能影響你的構建。

它是decimal類型,提供decimal <===> int轉換。 當它返回到你的代碼時 - checked關鍵字可能會產生影響 - 它已經被拋出了int或異常。

C#自定義運算符支持不會擴展到允許您添加單獨的已檢查/未檢查的運算符實現,遺憾的是。

C#規范(第12.7.14節“已檢查和未檢查的運算符”)包含受影響的運算符和語句的列表。 測試中的操作員不在列表中:

以下操作受已checkedunchecked運算符和語句建立的溢出檢查上下文的影響:

  • 當操作數是整數或枚舉類型時,預定義的++--運算符(第12.7.10節和第12.8.6節)。
  • 預定義-一元運算符(第12.8.3節),當操作數是整數類型時。
  • 預定義的+-*/二元運算符(第12.9節),當兩個操作數都是整數或枚舉時。
  • 顯式數字轉換(第11.3.2節),從一個整數或枚舉類型到另一個整數或枚舉類型,或從floatdouble float到整數或枚舉類型。

CLR為簡單的算術運算提供IL指令,如add (加法), sub (減法), mul (乘法), div (除法)。

例如,讓我們使用add指令,它將兩個值相加。 add指令不執行溢出檢查,但是有一個名為add.ovf指令,它也會將兩個值相加,但如果發生溢出則會拋出OverflowException

因此,當您使用checked運算符,語句或編譯器開關時,它將使用“溢出檢查”版本的add指令( add.ovf )。

請記住,這僅適用於“原始類型”

但是 decimal的東西差別不大。 decimal類型不被CLR視為基本類型(盡管編程語言如c#或visual basic),這意味着CLR沒有知道如何操作decimal值的IL指令。 如果在.NET Framework SDK Documantation中查找decimal類型或在ReferenceSources中查找源代碼 - 您會注意到,有一些名為Add, Subtract, Multiply, Divide, etc..方法以及+, -, *, /, etc運算符重載方法+, -, *, /, etc

編譯使用decimal代碼時,編譯器將生成代碼以調用decimal成員來執行實際操作。 此外,由於沒有用於操作decimal值的IL指令,因此checked/unchecked運算符/語句/編譯器開關無效。 如果無法安全執行操作,則使用decimal值的操作將始終拋出OverflowException

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM