2017-04-05 55 views
0

假設default算術溢出(未)檢查,下面的代碼是否保證外部檢查/未檢查的上下文影響其內部創建的lambdas的行爲?

Action<Int32[]> action; 

checked 
{ 
    action = array => 
     Console.WriteLine(array[0] + array[1]); 
} 

var items = new[] 
{ 
    Int32.MaxValue, 
    Int32.MaxValue 
}; 
action(items); 

將導致

System.OverflowException:算術運算導致溢出..

如果我們將項目設置設置爲/checked,並將checked {替換爲unchecked {,則不會拋出異常。

那麼,我們可以依靠這種行爲,還是對array => unchecked (array[0] + array[1])更安全?

+0

你想保證C#編譯器沒有任何錯誤? github網站列出了*一萬個問題,其中三分之一仍然是開放的。碰巧遇到[這一個](https://github.com/dotnet/roslyn/issues/18446),當我看着剛纔:) :) –

+0

@HansPassant據我所見,這裏沒有錯誤 - 一切正常罰款和相當邏輯。但是否可以依靠這種行爲作爲標準,這讓我很困擾。 [@InBeetween的答案](http://stackoverflow.com/a/43225470/3745022)中提到的規範有點含糊不清,不管lambda是否保證受到影響仍然存在一些疑問。 –

+0

當它不起作用時就回來,你有一個具體問題向我們展示,SO總是在附近。 –

回答

1

在最後正式公佈C#規範,它說以下內容:

8.11(...)選中的語句使塊中的所有表達式在checked上下文中進行評估,而unchecked語句使塊中的所有表達式都在未經檢查的上下文中進行評估。 (...)

我想說很自信地說action將始終處於選中/取消上下文這是你所看到的當前行爲進行評估,我不希望這在未來改變。

要展開多一點我的答案,如果你檢查的編譯代碼,你會看到Console.WriteLine(array[0] + array[1])checked語句中實際上是編譯成的Console.WriteLine(checked (array[0] + array[1]))相當於所以實在沒有必要自己,做編譯器會做它反正。

+0

好吧,我們似乎可以從字面上理解'8.11',所以當lambda的身體處於檢查的環境中時,它應該受到它的影響。謝謝,儘管我可能會繼續在lambda中應用'(un)checked',但它肯定是可靠的,並且它更加本地化/更短。 –

0

請注意,checkedunchecked更改編譯器發出的指令。例如。在IL中有add指令的兩個(實際上更多的)變體,其中一個變體忽略溢出,而另一個檢查的溢出。

由於它改變了發射的IL,所以它必須適用。


例如,驗證碼:

static void Main(string[] args) 
    { 
     int i = 0; 
     int j = 1; 
     int k; 
     checked 
     { 
      k = i + j; 
     } 
     unchecked 
     { 
      k = i + j; 
     } 
     Console.ReadLine(); 
    } 

射向IL:

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    .maxstack 2 
    .locals init (
     [0] int32 num, 
     [1] int32 num2, 
     [2] int32 num3) 
    L_0000: nop 
    L_0001: ldc.i4.0 
    L_0002: stloc.0 
    L_0003: ldc.i4.1 
    L_0004: stloc.1 
    L_0005: nop 
    L_0006: ldloc.0 
    L_0007: ldloc.1 

    L_0008: add.ovf 

    L_0009: stloc.2 
    L_000a: nop 
    L_000b: nop 
    L_000c: ldloc.0 
    L_000d: ldloc.1 

    L_000e: add 

    L_000f: stloc.2 
    L_0010: nop 
    L_0011: call string [mscorlib]System.Console::ReadLine() 
    L_0016: pop 
    L_0017: ret 
} 

在那裏你可以看到發出的兩種不同的指令。

+0

是的,我知道如何檢查/取消選中作品。我只是想知道它是否能保證以相同的方式影響在(未)檢查的上下文中爲lambda表達式生成的方法。 –

+0

@EugenePodskal - 然後我誤解了困惑。如果有人認爲'checked'是一個隱藏溢出異常的'try' /'catch',就可以理解混淆;這是它在運行時實現的,因此重要的是,lambda中的代碼不會與'checked'塊在* temporal範圍*中執行。 –

0

也許最好把C#想象成具有兩組整數運算符,其中一個執行溢出檢查,另一個不執行; 「+」運算符是否綁定到「溢出檢查的附加」運算符或「包裝添加」添加運算符是由它出現在選中還是未選中的上下文中控制。操作符影響程序執行的唯一方法是選擇哪些操作符綁定到「+」之類的標記;當代碼由編譯器檢查時發生這種綁定,而不是在運行時發生。