常见的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也就会一直活着)

  

原文地址:https://www.cnblogs.com/PurpleTide/p/1929735.html