2011-06-01 26 views
8

我在一個局部變量中存儲了一個動作,然後我使用該局部變量超出了範圍。在使用之前它有被清理的危險嗎?這裏有一個例子:Action/Lambda表達式內存管理問題

public List<object> GetMaps() { 
    Action<Customer1, Customer2> baseMap = (Customer1 c1, Customer2 c2) => { 
     c2.FirstName = c1.FirstName; 
    }; 

    var list = new List<object>() { 
     new Action<SpecialCustomer1 c1, SpecialCustomer2 c2>() { 
      baseMap(c1, c2); 
      c2.SpecialProperty = c1.SpecialProperty; 
     }, 
     new Action<SpecialCustomer1 c1, SpecialCustomer2 c2>() { 
      baseMap(c1, c2); 
      c2.SpecialProperty2 = c1.SpecialProperty2; 
     }, 
    }; 

    return list; 
} 

所以,你可以在這個例子中看到,函數返回的調用baseMap操作的列表。 baseMap只是一個局部變量。是否在.NET的其他行爲中被稱爲不清除它的事實?

回答

11

我是指你的C#4的規格,它說的第5.1.7節:

如果局部變量被匿名函數捕獲,它的生存期至少延長到從匿名函數創建的委託或表達式樹以及引用捕獲變量的任何其他對象都有資格進行垃圾回收。

即使控制通過局部範圍的結束,本地的生命週期也會延長。在實踐中,我們通過將本地變爲閉包類的字段來實現此目的,然後通過在委託(或表達式樹)中引用它來保持該類的活性。

但請注意,您可能會遇到相反的問題;有時事情活得比你希望他們:

Expensive expensive = new Expensive(); 
Cheap cheap = new Cheap(); 
Action longlived =()=>M(cheap); 
Action shortlived =()=>M(expensive); 

今天這個在C#中的工作方式是兩個代表產生的閉合不斷既「便宜」與「貴」活着只要長壽命即使較長的委託實際上並沒有使用「昂貴」的代理, (VB,JScript和其他許多有閉包的語言也有這個問題。)

我們在這裏可以做的是在編譯器中檢測這種情況並創建兩個閉包。我們正在考慮爲未來版本的C#做這件事。

5

不,它沒有危險。使用匿名方法外部的局部變量的匿名方法將被編譯爲一個新類,其中包含一個保存該局部變量的字段以及一個與匿名方法相對應的方法。

在你的情況,這將創建類似如下:

class ModifiedClosure 
{ 
    private Action<Customer1, Customer2> _baseMap; 
    public ModifiedClosure(Action<Customer1, Customer2> baseMap) 
    { 
     _baseMap = baseMap; 
    } 

    public void Method(SpecialCustomer1 c1, SpecialCustomer2 c2) 
    { 
     _baseMap(c1, c2); 
     c2.SpecialProperty = c1.SpecialProperty; 
    } 
} 

列表初始化隨後將是這個樣子:

Action<Customer1, Customer2> baseMap = (c1, c2) => c2.FirstName = c1.FirstName; 

var list = new List<object>() 
{ 
    (Action<SpecialCustomer1, 
      SpecialCustomer2>)(new ModifiedClosure(baseMap).Method), 
    // ... 
}; 

BTW:你的語法是有點過。列表創建將不會編譯。它應該是這樣的:

var list = new List<object>() 
{ 
    (Action<SpecialCustomer1, SpecialCustomer2>)((c1, c2) => 
    { 
     baseMap(c1, c2); 
     c2.SpecialProperty = c1.SpecialProperty; 
    }), 
    (Action<SpecialCustomer1, SpecialCustomer2>)((c1, c2) => 
    { 
     baseMap(c1, c2); 
     c2.SpecialProperty2 = c1.SpecialProperty2; 
    }) 
}; 
0

委託是引用類型,不會被清理,直到將有來自根至少一個參考給他們。所以如果你創建了Action委託,並且你將從方法中傳出委託,那麼不要擔心清理問題。

1

它不在範圍之內,因爲它被列表引用,所以它不會被垃圾收集器清理。

所以,不,你不會有危險