积极答复者
一个Linq 的 ToList()问题

问题
-
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
List<CategoryLevel> allLevels = new List<CategoryLevel>();
allLevels.Add(new CategoryLevel
{
CategoryLevelId = 1,
CategoryId = 1,
LevelName = "Level1"
});
allLevels.Add(new CategoryLevel
{
CategoryLevelId = 2,
CategoryId = 2,
LevelName = "Level2"
});
List<Category> allCategories = new List<Category>();
allCategories.Add(new Category
{
CategoryId = 1
});
allCategories.Add(new Category
{
CategoryId = 2
});
foreach (var category in allCategories)
{
category.CategoryLevels = allLevels.Where(l => l.CategoryId == category.CategoryId); //为什么如果这里加ToList() 后就不会失败
}
var checkedLevels = allCategories.Where(c => c.CategoryId == 1).First().CategoryLevels;
Debug.Assert(checkedLevels.Where(l => l.CategoryLevelId == 2).Count() == 0); //断言失败, 为什么???
}
}
internal class Category
{
public int CategoryId { get; set; }
public IEnumerable<CategoryLevel> CategoryLevels { get; set; }
}internal class CategoryLevel
{
public int CategoryLevelId { get; set; }
public int CategoryId { get; set; }
public string LevelName { get; set; }
}
}- 已移动 mldark 2010年4月1日 7:51 (发件人:Visual C#)
答案
-
这是一个延迟执行(Deferred excution)的问题。当调用where时,实际得到的不是集合,而是运算表达式,直到ToList时,他才成为对应的集合。在你的循环中,运算式中的变量值发生了变化(category),等同于如下代码,所以 levellist1 levellist2在执行时运算了相同的结果。
var cate1 = allCategories[0];
var level1 = allLevels.Where(l => l.CategoryId == cate1.CategoryId);cate1 = allCategories[1];
var level2 = allLevels.Where(l => l.CategoryId == cate1.CategoryId);var levellist1 = level1.ToList();
var levellist2 = level2.ToList();你改成这样,结果就对了
var cate1 = allCategories[0];
var level1 = allLevels.Where(l => l.CategoryId == cate1.CategoryId);var levellist1 = level1.ToList();
cate1 = allCategories[1];
var level2 = allLevels.Where(l => l.CategoryId == cate1.CategoryId);var levellist2 = level2.ToList();
其实,也就对应于你把 ToList() 放到循环里。所以,在使用deferred excution时,小心中间变量变化的副作用。
Mog Liang- 已标记为答案 Mog Liang 2010年4月7日 8:37
全部回复
-
这是一个延迟执行(Deferred excution)的问题。当调用where时,实际得到的不是集合,而是运算表达式,直到ToList时,他才成为对应的集合。在你的循环中,运算式中的变量值发生了变化(category),等同于如下代码,所以 levellist1 levellist2在执行时运算了相同的结果。
var cate1 = allCategories[0];
var level1 = allLevels.Where(l => l.CategoryId == cate1.CategoryId);cate1 = allCategories[1];
var level2 = allLevels.Where(l => l.CategoryId == cate1.CategoryId);var levellist1 = level1.ToList();
var levellist2 = level2.ToList();你改成这样,结果就对了
var cate1 = allCategories[0];
var level1 = allLevels.Where(l => l.CategoryId == cate1.CategoryId);var levellist1 = level1.ToList();
cate1 = allCategories[1];
var level2 = allLevels.Where(l => l.CategoryId == cate1.CategoryId);var levellist2 = level2.ToList();
其实,也就对应于你把 ToList() 放到循环里。所以,在使用deferred excution时,小心中间变量变化的副作用。
Mog Liang- 已标记为答案 Mog Liang 2010年4月7日 8:37