2009-10-28 133 views
13

這不是Calling a method with ref or out parameters from an anonymous method爲什麼在匿名方法中不允許out參數?

愚弄的人,爲什麼出參數沒有匿名方法內不允許我想知道。不允許ref參數對我來說有點意義,但參數,沒有那麼多。

對此有何看法

+0

我想知道爲什麼你會傳遞一個方法體外的匿名類型,除非你想學習反射的喜悅。 – Will 2009-10-28 15:06:03

+0

原始鏈接問題上的這個答案對此沒有充分回答嗎? http://stackoverflow.com/questions/801147/scope-of-anonymous-methods/801563#801563 – 2009-10-28 15:09:27

+1

@威爾:這與匿名類型無關。 – SLaks 2009-10-28 15:16:48

回答

28

在某些方面,這是一個騙局。 Out參數是ref參數。 C#語言使用的值只有一個額外的屬性。禁止它們的原因與ref參數完全相同。

此處的問題源於使用匿名方法內的匿名方法外聲明的值的效果。這樣做會捕捉lambda中的值,並且出於必要性,將其壽命延長到超出當前函數的壽命。這與具有固定使用壽命的out參數不兼容。

想象一下,例如out參數引用堆棧上的局部變量。 lambda可以在將來的任意點執行,因此可以在堆棧幀不再有效時執行。 out參數意味着什麼?

+1

+1已經開始輸入類似於您最後一段的示例。感謝您節省我的麻煩:) – 2009-10-28 15:12:44

+0

第二段打開了燈泡。謝謝@JaredPar! – 2009-10-28 15:20:49

+0

但是這不適用於其他類型的變量嗎?我的意思是,如果我在位於堆棧中的lambda表達式中使用一個變量,只需訪問某個字段即可。如果lambda表達式在堆棧不再有效時執行,該對象是否已被收集?如果不是,垃圾收集器何時收集該對象? – 2009-10-28 15:40:00

6

這基本上是與事實有關的匿名委託/ lambda表達式的參數捕獲變量,並捕捉ref/out變量不會使在C#/ CLR的任何意義,因爲它需要ref/out字段內部。另外請注意,我將這兩個關鍵字配對,因爲它們實際上是相同的。

如果你想在他的博客上有一個完整的解釋,Eric Lippert discussed this design point in detail。 (見段落附近特別是底部)

1

outref參數之間的唯一區別是一個out參數將具有施加到它的[out]令牌。就CLR而言,它們是同樣的事情。

爲了實現它,編譯器將不得不生成ref字段,這是不支持的。

如果你仔細想想,你會意識到讓一個匿名方法使用out參數是沒有意義的。

下面的代碼會怎樣?

static Func<object, object> Mess(out object param) { 
    param = "Original"; 
    return i => param = i; 
} 
static Func<object, object> MessCaller() { 
    object local; 
    return Mess(out local); 
} 
static vouid Main() { 
    Console.WriteLine(MessCaller()("New")); 
    //The local variable that the lambda expression writes to doesn't exist anymore. 
} 
+1

'OutAttribute'屬性不適用於C#中out參數。 'OutAttribute'是編組東西時考慮的自定義屬性。 'out'參數由另一個元數據標記(IL中的'[out]')註釋。您可以通過查詢C#代碼中聲明爲'out'的'ParameterInfo'的屬性來驗證這一事實。那裏沒有'[OutAttribute]'。 – 2009-10-28 15:24:42

+0

固定;謝謝。 – SLaks 2009-10-28 15:31:05

1

我在開發一些錯誤處理代碼時遇到了這個難題。我想將一個引用(out)傳遞給一個會被記錄的錯誤消息。這讓我的匿名方法有機會執行多個檢查,每個檢查都根據需要設置錯誤消息。

我最終爲工作不同的匿名方法編寫了一個新的包裝器。但是,我認爲對某人來說可能有一些價值,那就是我可以簡單地創建一個具有out參數的私有方法,並定義一個委託,並使我的代碼使用它。希望這有助於/激勵某人。

protected delegate void OutStringDelegate(int divider, out string errorText); 
    protected void codeWrapper(int divider, OutStringDelegate del) 
    { 
     string ErrorMessage = "An Error Occurred."; 

     try 
     { 
      del(divider, out ErrorMessage); 
     } 
     catch 
     { 
      LogError(ErrorMessage); 
     } 
    } 
    public void UseWrapper(int input) 
    { 
     codeWrapper(input, codeToCall); 
    } 
    private int somePrivateValue = 0; 
    private void codeToCall(int divider, out string errorMessage) 
    { 
     errorMessage = "Nice Error Message here!"; 
     somePrivateValue = 1/divider; // call me with zero to cause error. 
    } 
    private void LogError(string msg) 
    { 
     Console.WriteLine(msg); 
    } 
相關問題