[英]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節“已檢查和未檢查的運算符”)包含受影響的運算符和語句的列表。 測試中的操作員不在列表中:
以下操作受已
checked
和unchecked
運算符和語句建立的溢出檢查上下文的影響:
- 當操作數是整數或枚舉類型時,預定義的
++
和--
運算符(第12.7.10節和第12.8.6節)。- 預定義
-
一元運算符(第12.8.3節),當操作數是整數類型時。- 預定義的
+
,-
,*
和/
二元運算符(第12.9節),當兩個操作數都是整數或枚舉時。- 顯式數字轉換(第11.3.2節),從一個整數或枚舉類型到另一個整數或枚舉類型,或從
float
或double
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.