2012-01-18 51 views
27

說我有下面的代碼:「fjuk」輸出參數和異常

static void Fjuk(out string str) 
    { 
     str = "fjuk!"; 
     throw new Exception(); 
    } 

    static void Main(string[] args) 
    { 
     string s = null; 
     try 
     { 
      Fjuk(out s); 
     } 
     catch (Exception) 
     { 
      Console.WriteLine(s ?? ""); 
     } 
    } 

當我測試了一下,s已經被初始化到當它在catch區塊中使用時。
這是由規範保證還是依賴於實現? (我已經搜查了C#3.0規範,但無法找到自己)

+0

我不知道規範,但它肯定是我期望的。我期望成員變量,屬性等的初始化也將在你的catch塊中可用。 – 2012-01-18 07:57:30

+1

Eric Lippert在什麼時候需要他...... :) – 2012-01-18 08:00:52

+0

@jb。 [MSDN]有什麼問題(http://msdn.microsoft.com/en-us/library/t3c3bfhx(v = vs.80).aspx)? – gdoron 2012-01-18 08:08:59

回答

23

差不多,即什麼out手段一個方面;首先,請注意out並不存在 - 我們只需要考慮refout僅僅是ref,在編譯器中有一些「明確的分配」調整)。 ref的意思是「傳遞這個地址」 - 如果我們通過地址更改值,那麼立即顯示 - 它畢竟是更新內存的堆棧Main。它不能抽象這個(延遲寫入),因爲這個值可能是,例如,一些特別用於避免將其複製到堆棧上的超大結構(在XNA等中廣泛使用的一種方法)。

5

它的「保證」因爲out參數改變與參數的memory address值。

out關鍵字導致參數通過引用傳遞。這與ref關鍵字類似,除了ref要求變量在被傳遞之前被初始化。

MSDN

4

如果該方法拋出異常,則不保證輸出參數被設置。如果該方法沒有異常退出,則保證輸出參數被設置。

在你的情況下,該方法將始終設置輸出參數,但編譯器不會以這種方式分析該方法的代碼。如果該方法退出並出現異常,則輸出參數仍然不被認爲是明確設置的。

您在異常處理程序中的代碼不依賴於由方法調用設置的變量,因爲您在創建變量時設置該變量。如果您在創建時沒有設置變量,異常處理程序不能使用它,因爲它不能保證設置:

string s; 
try { 
    Fjuk(out s); 
    Console.WriteLine(s); // here the variable is guaranteed to be set 
} catch (Exception) { 
    Console.WriteLine(s); // here it's not, so this won't compile 
} 
+0

是的,我認爲它很明顯,它不檢查被調用方法中的代碼,但沒有在我的例子中說清楚。感謝您指出了這一點! – Niklas 2012-01-18 11:36:39

1

它從Fjuk角度保證,但不是Main

Fjuk設置參數後拋出異常。儘管可以通過編譯器,抖動和CPU完成重新排序,但不會有重新排序,以致單個線程觀察到的順序發生變化。由於單個線程可能會「注意到」在拋出異常之前未設置參數,因此參數將保證設置。

儘管在Main中,我們並不知道Fjuk的實現細節,所以當編譯器分析Main時,它不能依賴於此。因此,在變化,我們不調用之前的值賦給s

static void Main() 
{ 
    string s; 
    try 
    { 
     Fjuk(out s); 
     Console.WriteLine(s ?? "");//fine 
    } 
    catch (Exception) 
    { 
     Console.WriteLine(s ?? "");//compiler error 
    } 
    Console.WriteLine(s ?? "");//compiler error 
} 

使用s後馬上打電話給Fjuk是好的,因爲一次只能到達那裏,如果Fjuk成功的第一次嘗試,如果Fjuk成功,則必須分配s。然而,在第二和第三種情況下,可以在沒有Fjuk成功的情況下到達那些行,並且由於無法通過分析Main分析是否可以在設置s之前拋出異常,因此必須禁止使用s