[英]Is Access to Modified Closure warnings valid for string variable?
我在访问字符串变量的修改后的闭包时发出警告。
foreach (string s in splits)
{
regexes.Where(x => x.Pattern.Contains(s));
}
以原样保留代码是否安全? 我假设使用lambda创建的委托将按值接收字符串,因为字符串是不可变的,并且每个新的“ s”都将指向不同的内存。
谢谢
在这种情况下,这不会对您造成伤害,因为您不在循环之外的地方使用委托。 当然,您在示例中并没有做任何有用的事情 ,因此尚不清楚这是否代表您的实际代码。
不过,线程和延迟的回调将受到影响,因此是的(在某种程度上)它是有效的。 如果不确定,请修复它:
foreach (string s in splits)
{
var clone = s;
regexes.Where(x => x.Pattern.Contains(clone));
}
为了明确起见,仅当在foreach循环的正常流程之外使用捕获的变量时,此问题才会受到伤害 。 当直接在循环内部且在同一线程(没有Parallel
)上使用时,捕获的变量将始终处于期望值。 任何破裂的东西都会看到随机数据; 通常(但不总是) 最后一个值。
特别要注意的是 ,此“延迟委托”包括诸如在查询上建立Where
过滤器之类的事情,因为您会发现它们都使用最后一个值。 以下是不好的:
var query = ...
foreach (string s in splits)
{ // BAD CODE!
query = query.Where(x => x.Foo.Contains(s));
}
重新进行任何“更好的方式”(评论)...好吧,克隆变量技巧具有工作的优势,但这是一个可爱的技巧-修复了上面的“错误代码!”:
query = splits.Aggregate(query, (tmp, s) => tmp.Where(x => x.Foo.Contains(s)));
我个人认为以下内容更容易理解:
foreach (string s in splits)
{
var tmp = s;
query = query.Where(x => x.Foo.Contains(tmp));
}
我假设使用lambda创建的委托将按值接收字符串,因为字符串是不可变的,并且每个新的“ s”都将指向不同的内存。
创建委托不会评估变量。 这就是闭包的全部要点:它们关闭变量 。 因此,您所有的lambda都将捕获相同的变量s
,这可能不是您想要的。 是否影响代码取决于lambda的评估时间:如果Where
的调用已经“执行”了lambda(然后将其丢弃),则应该没事。 如果您的正则regexes
对象存储了lambda,并在以后使用它们,则您会遇到麻烦。
因此,为了安全起见,首先将值复制到循环内部的局部变量中。
看起来并不安全,我通常会尽量避免出现警告,因为它们通常表示潜在的问题。
谷歌搜索"access to modified closure"
带来了许多看似相关的热门话题,包括#2 Linq:当心您肯定要仔细研究的“访问修改后的闭包”恶魔 。
这是一个简单的例子,证明这对于不可变的字符串是不安全的。
List<Func<int>> lambdas = new List<Func<int>>();
List<string> strings = new List<string>
{ "first", "second", "supercalifragilisticexpialidocious" };
foreach (string s in strings)
{
lambdas.Add(new Func<int>(() => s.Length));
}
Console.WriteLine(lambdas[0]()); //34
Console.WriteLine(lambdas[1]()); //34
Console.WriteLine(lambdas[2]()); //34
但是,除了被LINQ的延迟执行所困扰之外,我真的不认为以这种方式使用lambda并不是问题,因为lambda的整个生命周期都在foreach的范围之内。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.