lan*_*nce 10 c# warnings list-comprehension resharper-6.0
ReSharper 6.0为我提供dr了第一个代码片段中标识符的"访问修改后的闭包"警告.
private IEnumerable<string> GetTheDataTableStrings(DataTable dt) {
foreach (DataRow dr in dt.Rows) {
yield return GetStringFuncOutput(() => dr.ToString());
}
}
Run Code Online (Sandbox Code Playgroud)
我想我已经基本了解了这个警告试图保护我的内容:dr在询问GetTheDataTableStrings的输出之前多次更改,因此调用者可能无法获得我期望的输出/行为.
但是R#没有给我第二个代码片段的任何警告.
private IEnumerable<string> GetTheDataTableStrings(DataTable dt) {
return from DataRow dr in dt.Rows select GetStringFuncOutput(dr.ToString);
}
Run Code Online (Sandbox Code Playgroud)
使用理解语法时,放弃此警告/关注是否安全?
其他代码:
string GetStringFuncOutput(Func<string> stringFunc) {
return stringFunc();
}
Run Code Online (Sandbox Code Playgroud)
Eri*_*ert 22
首先,你关注第一个版本是正确的.由该lambda创建的每个委托都在同一个变量上关闭,因此当该变量发生变化时,查询的含义会发生变化.
其次,仅供参考,我们很有可能在下一版本的C#中解决这个问题; 这对开发人员来说是一个重大的痛点.
在下一个版本中,每次运行"foreach"循环时,我们将生成一个新的循环变量,而不是每次都关闭相同的变量.这是一个"突破"的变化,但在绝大多数情况下,"休息"将是修复而不是导致错误.
"for"循环不会改变.
有关详细信息,请参见http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/.
第三,查询理解版本没有问题,因为没有被修改的关闭变量.查询理解表与您说的一样:
return dt.Rows.Select(dr=>GetStringFuncOutput(dr.ToString));
Run Code Online (Sandbox Code Playgroud)
lambda不会关闭任何外部变量,因此不会意外修改变量.
Resharper警告的问题已在C#5.0和VB.Net 11.0中得到解决.以下是语言规范的摘录.请注意,默认情况下,在安装了Visual Studio 2012的计算机上,可以在以下路径中找到规范.
C#语言规范版本5.0
8.8.4 foreach声明
在while循环中放置v对于嵌入式语句中发生的任何匿名函数如何捕获它很重要.
例如:
int[] values = { 7, 9, 13 };
Action f = null;
foreach (var value in values)
{
if (f == null) f = () => Console.WriteLine("First value: " + value);
}
f();
Run Code Online (Sandbox Code Playgroud)
如果v在while循环之外声明,它将在所有迭代之间共享,并且它在for循环之后的值将是最终值13,这将是f的调用打印.相反,因为每次迭代都有自己的变量v,在第一次迭代中由f捕获的那个将继续保持值7,这将是要打印的值.(注意:早期版本的C#在while循环之外声明了v.)
Microsoft Visual Basic语言规范版本11.0
10.9.3 For Each ...下一个语句(注释)
版本10.0和11.0之间的行为略有变化.在11.0之前,没有为循环的每次迭代创建新的迭代变量.仅当迭代变量由lambda或LINQ表达式捕获时才能观察到这种差异,然后在循环之后调用该表达式.
Dim lambdas As New List(Of Action)
For Each x In {1,2,3}
lambdas.Add(Sub() Console.WriteLine(x)
Next
lambdas(0).Invoke()
lambdas(1).Invoke()
lambdas(2).Invoke()
Run Code Online (Sandbox Code Playgroud)
到Visual Basic 10.0,这在编译时产生了一个警告并且打印了"3"三次.那是因为循环的所有迭代只共享一个变量"x",并且所有三个lambdas都捕获了相同的"x",并且当lambdas被执行时它然后保持数字3.从Visual Basic开始11.0,打印"1,2,3".那是因为每个lambda捕获一个不同的变量"x".