常見的Lambda表達式引起的閉包問題
以下代碼的原意是想找出list中滿足Name字段包含t, Remark字段包含mark的數據
代碼
//構造的搜索條件 希望Name包含t remark中包含mark中的文本
Dictionary<string, string> forms = new Dictionary<string, string>();
forms.Add("Name", "t");
forms.Add("Remark", "mark");
//構造一些數據 理論上全部數據都應該滿足上面的那個條件 (忽略大小寫)
List<UserInformation> list = new List<UserInformation>() {
new UserInformation(){Name ="Test1", Remark="Remark1"},
new UserInformation(){Name ="Test2", Remark="Remark2"},
new UserInformation(){Name ="Test3", Remark="Remark3"},
};
var lambda = list.AsQueryable();
foreach (string key in forms.Keys)
{
string val = forms[key];
if (key == "Name")
lambda = lambda.Where(p => p.Name.Contains(val));//原意是 在條件是Name的時候 對Name字段做過濾
if (key == "Remark")
lambda = lambda.Where(p => p.Remark.Contains(val));//原意是 在條件是Remark的時候 對Remark字段做過濾
}
var data = lambda.ToList();//大家可以注意到結果是0條
var data2 = list.AsQueryable().Where(p => p.Name.Contains(forms["Name"])).Where(p => p.Remark.Contains(forms["Remark"])).ToList();//這個的結果是3條
不過實際情況是data中間一條記錄都沒有
而hardcode算出來的data2中有3條記錄
原因如下:
這個lambda表達式 Where(p=>p.Name.Contains(val)) , 實際上只是保留了一個指向函數外部的val的引用 , 他這個時候并沒有把val的真實的值拷貝進來
真正去讀取val值的時候是 lambda.ToList() 這個時候才真正執行lambda表達式取數據,過濾數據 ,也是這個時候才去讀取val的值
而在foreach的第二次操作的時候 val的值被覆蓋mark了 那么就造成了 原來的Where(p=>p.Name.Contains(val)) 變成了 Where(p=>p.Name.Contains("mark"))
注意那個值是mark而不是t
如果我們把三條數據的Name都改成markdafafafadsf 之類的值 那么再次計算data的數據就會變成三條, 大家可以自己弄一下試試,
作用域:
本來val是一個臨時變量,他的生命周期應該在foreach結束以后就結束了
但是 由于他被閉包引用,那么val的生命周期延長到引用對象的生命周期(那個lambda不死....val也就會一直活著)

浙公網安備 33010602011771號