使用IEnumerable.Where()循环遍历一组`XElements`时会产生意外的输出

时间:2012-11-01 10:00:47

标签: c# console console-application ienumerable xelement

我在循环浏览一组XElements时遇到问题。这种行为不符合我的预期。

我的代码的简短重写版本,因此您可以轻松解决我的问题(C#中的控制台应用程序)

IEnumerable<XElement> q = from c in xml.Descendants(aw + "wd") 
                          where (....) 
                          select c;    
...

//--------------------------------------------------------------------    
IEnumerable<XElement> currRow = q.OrderBy(yyy => (int)yyy.Attribute("t"));

int xValue = 10;

currRow = currRow.Where(yyy => (int)yyy.Attribute("t") < xValue);
xValue = 20; 
//Here, the currRow gets a new value automatically. I don't want this!


//--------------------------------------------------------------------
//This is want i want to acheive: 

IEnumerable<XElement> currRow = q.OrderBy(yyy => (int)yyy.Attribute("t")); 

int xValue = 10;

currRow = currRow.Where(yyy => (int)yyy.Attribute("t") < xValue);
//do somthing with currRow

xValue = 20; 
currRow = currRow.Where(yyy => (int)yyy.Attribute("t") < xValue);
//do somthing else with currRow

xValue = 30; 
currRow = currRow.Where(yyy => (int)yyy.Attribute("t") < xValue);
// etc....

有什么想法吗?

5 个答案:

答案 0 :(得分:1)

每当您拨打.where并将结果分配给currRow时,您将限制该组。

所以第一行

 currRow = currRow.Where(yyy => (int)yyy.Attribute("t") < xValue);

将限制所有t&lt; 10.然后再打电话

 xValue = 20;
 currRow = currRow.Where(yyy => (int)yyy.Attribute("t") < xValue);

你会得到完全相同的结果,因为所有元素由于第一次调用Where和后续赋值给currRow,因此currRow的值小于 10 / p>

你可以这样做

   const int stepSize = 10;
   var iterations = 3
    for(var i = 0;i<iterations;++i){
         var curr = currRow.Where(yyy => (int)YYY.Attribute("t") >= i * stepSize 
                                       && (int)YYY.Attribute("t") < (i+1) * stepSize);
         //do sometihng with curr
    }

评论后编辑

使用枚举器时评估的位置

xValue = 10;
currRow = currRow.Where(yyy => (int)YYY.Attribute("t") > xValue);

foreach(var elem in currRow){
    //this iterates all elements with t < 10;
}

xValue = 20; 
foreach(var elem in currRow){ //notice that currRow hasn't been reassigned
    //this iterates all elements with t < 20;
}

你必须选择避免这种设计的行为

要么你可以避免重新分配xValue,要么你可以像这样执行查询

var list = currRow.Where(yyy => (int)YYY.Attribute("t") > xValue).ToList();

最后注意.ToList()

答案 1 :(得分:0)

IEnumerable<XElement> currRow = q.OrderBy(yyy => (int)yyy.Attribute("t")); 

int xValue = 10;    

var abc = currRow.Where(yyy => (int)yyy.Attribute("t") < xValue);
//Do something with abc , abc is IEnumerable<XElement> list;

答案 2 :(得分:0)

currRow.Where(...)

将创建一个将在使用时执行的查询 如果您想直接得到结果(并且省略任何引用/查询),请将其转换为列表。

currRow.Where(...).ToList()

你应该这样做的原因是你在where语句中使用的值在查询中不被视为常量,而是对你使用的变量的引用。如果更改变量,则可能会更改查询的结果 如果您调用.ToList(),则会使用该时刻的变量值执行查询。结果被收集并且是“永久的”,使得当查询中使用的变量的值发生变化时,创建结果的查询将不再影响结果。

答案 3 :(得分:0)

看起来你的xValue被捕获到where语句的lambda表达式中。为什么不简单地提供恒定值?

currRow = currRow.Where(yyy => (int)yyy.Attribute("t") < 20);
//do somthing with currRow
currRow = currRow.Where(yyy => (int)yyy.Attribute("t") < 10);
//do somthing with currRow

也不要以最小过滤值< 10开头。将结果分配给currRow变量时,其他过滤器将无效,因为结果已经小于20且小于30

答案 4 :(得分:0)

目前我的机器上没有VS进行测试,但可能是由于延迟加载。

在LINQ表达式中使用xValue时,它通过引用而不是值传递。这意味着在枚举LINQ表达式之前,无论何时更改xValue,您都会无意中更改LINQ表达式。

这篇文章(Are values in LINQ expressions passed by reference?)似乎支持我的理论。尝试将select语句更改为以下内容,它可能会为您提供您期望的行为:

currRow = currRow.Where( yyy => ( int ) yyy.Attribute( "t" ) < xValue ).Select( x => x );