我无法弄清楚为什么找不到我的过滤列表中的项目。我简化了示例来展示它。我有一个班级项目......
public class Item
{
public Item(string name)
{
Name = name;
}
public string Name
{
get; set;
}
public override string ToString()
{
return Name;
}
}
...以及一个“项目”类,它应该过滤项目并检查第一项是否仍然在列表中......
public class Items
{
private IEnumerable<Item> _items;
public Items(IEnumerable<Item> items)
{
_items = items;
}
public List<Item> Filter(string word)
{
var ret = new List<Item>(_items.Where(x => x.Name.Contains(word)));
Console.WriteLine("found: " + ret.Contains(_items.First()));
// found: false
return ret;
}
}
执行代码如下所示:
static void Main(string[] args)
{
string[] itemNames = new string[] { "a", "b", "c" };
Items list = new Items(itemNames.Select(x => new Item(x)));
list.Filter("a");
Console.ReadLine();
}
现在,如果我执行程序,则Console.WriteLine输出找不到该项目。但为什么呢?
如果我将构造函数中的第一行更改为
_items = items.ToList()
然后,它可以找到它。如果我撤消该行并在Filter-method中稍后调用ToList(),它也找不到该项?!
public class Items
{
private IEnumerable<Item> _items;
public Items(IEnumerable<Item> items)
{
_items = items;
}
public List<Item> FilteredItems
{
get; set;
}
public List<Item> Filter(string word)
{
var ret = new List<Item>(_items.Where(x => x.Name.Contains(word)));
_items = _items.ToList();
Console.WriteLine("found: " + ret.Contains(_items.First()));
// found: false
return ret;
}
}
为什么执行lambda表达式的地点和时间以及为什么不再找到该项目有什么不同?我不明白!
答案 0 :(得分:20)
原因是延迟执行。
您将SELECT * FROM people WHERE name LIKE '%Esteban%Ricardo%';
字段初始化为
_items
这是查询,而不是该查询的答案。每次迭代itemNames.Select(x => new Item(x));
时,此查询都会执行。
所以在_items
方法的这一行:
Filter
枚举源数组并为每个字符串创建var ret = new List<Item>(_items.Where(x => x.Name.Contains(word)));
。这些项目存储在您的列表new Item(x)
。
在此之后致电ret
时,Contains(_items.First())
再次执行First()
中的查询,创建 new <每个源字符串的/ strong> _items
个实例。
由于Item
的{{1}}方法可能未被覆盖并执行简单的引用相等性检查,因此从第二次迭代返回的第一个Item
是{{1}的不同实例而不是列表中的那个。
答案 1 :(得分:10)
让我们删除额外的代码以查看问题:
var itemNames = new [] { "a", "b", "c" };
var items1 = itemNames.Select(x => new Item(x));
var surprise = items1.Contains(items1.First()); // False
集合items1
似乎不包含其初始元素! (demo)
添加ToList()
可解决问题:
var items2 = itemNames.Select(x => new Item(x)).ToList();
var noSurprise = items2.Contains(items2.First()); // True
您在使用和不使用ToList()
时看到不同结果的原因是(1)items1
被懒惰地评估,以及(2)您的Item
类未实现{{1} } / Equals
。使用GetHashCode
使默认平等工作;实现自定义相等性检查将解决多个枚举的问题。
本练习的主要教训是存储传递给构造函数的ToList()
是危险的。这只是其中一个原因;其他原因包括多次枚举以及在代码验证其输入后序列的可能修改。您应该对传递给构造函数的序列调用IEnumerable<T>
或ToList
以避免这些问题:
ToArray
答案 2 :(得分:4)
您的代码中存在两个问题。
第一个问题是您每次都要初始化一个新项目。也就是说,当你写作时,你不会在这里存储实际的项目。
IEnumerable<Item> items = itemNames.Select(x => new Item(x));
Select
的执行被推迟。即每次拨打.ToList()
时,都会使用itemNames
作为来源创建一组新项目。
第二个问题是您在此处通过引用比较项目。
Console.WriteLine("found: " + ret.Contains(_items.First()));
当您使用ToList
时,您将项目存储在列表中,并且引用保持不变,因此您将找到参考项目。
当您不使用ToList
时,引用不再相同。因为每次创建一个新项目。你无法找到不同参考的项目。