Rik*_*iko 2 c# linq linq-to-objects
我有一个关于在循环中定义的Linq表达式的范围问题.以下LinqPad C#程序演示了这种行为:
void Main()
{
string[] data=new string[] {"A1", "B1", "A2", "B2" };
string[] keys=new string[] {"A", "B" };
List<Result> results=new List<Result>();
foreach (string key in keys) {
IEnumerable<string> myData=data.Where (x => x.StartsWith(key));
results.Add(new Result() { Key=key, Data=myData});
}
results.Dump();
}
// Define other methods and classes here
class Result {
public string Key { get; set; }
public IEnumerable<string> Data { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
基本上,"A"应该有数据[A1,A2]和"B"应该有数据[B1,B2].
但是,当您运行此"A"获取数据[B1,B2]时,BIe也会为所有Result实例计算最后一个表达式.
鉴于我在循环中声明了"myData",为什么它表现得好像我在循环之外声明它?如果我这样做的话,它表现得像我期望的那样:
void Main()
{
string[] data=new string[] {"A1", "B1", "A2", "B2" };
string[] keys=new string[] {"A", "B" };
List<Result> results=new List<Result>();
IEnumerable<string> myData;
foreach (string key in keys) {
myData=data.Where (x => x.StartsWith(key));
results.Add(new Result() { Key=key, Data=myData});
}
results.Dump();
}
// Define other methods and classes here
class Result {
public string Key { get; set; }
public IEnumerable<string> Data { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
如果我在迭代中强制进行评估,我会得到所需的结果,这不是我的问题.
我问为什么"myData"似乎在迭代中共享,因为我在一次迭代的范围内声明了它?
有人打电话给Jon Skeet ......; ^)
它不是myData分享的 - 它是key.由于其中的值myData被懒惰地评估,它们取决于当前的值key.
它的行为方式是因为迭代变量的范围是整个循环,而不是循环的每次迭代.你已经有了一个单一 key变量,其值的变化,它的变化是由lambda表达式捕获.
正确的解决方法是只为了迭代变量拷贝到一个变量内循环:
foreach (string key in keys) {
String keyCopy = key;
IEnumerable<string> myData = data.Where (x => x.StartsWith(keyCopy));
results.Add(new Result() { Key = key, Data = myData});
}
Run Code Online (Sandbox Code Playgroud)
有关此问题的更多信息,请参阅Eric Lippert的博客文章"关闭循环变量被认为有害":第一部分,第二部分.
这是语言设计方式的一个不幸的神器,但现在改变它将是一个坏主意IMO.虽然任何改变行为的代码基本上都会事先被破坏,但这意味着(例如)C#6中的正确代码是有效的,但 C#5中的代码不正确,这是一个危险的位置.
| 归档时间: |
|
| 查看次数: |
251 次 |
| 最近记录: |