I just can't figure out why the item in my filtered list is not found. I have simplified the example to show it. I have a class Item...
public class Item
{
public Item(string name)
{
Name = name;
}
public string Name
{
get; set;
}
public override string ToString()
{
return Name;
}
}
... and a class 'Items' which should filter the items and check if the first item is still in the list...
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;
}
}
The executing code looks like this:
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();
}
Now, if I execute the program, the Console.WriteLine outputs that the item is not found. But why?
If I change the first line in the constructor to
_items = items.ToList()
then, it can find it. If I undo that line and call ToList() later in the Filter-method, it also cannot find the item?!
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;
}
}
Why is there a difference where and when the lambda expression is executed and why isn't the item found any more? I don't get it!
The reason is deferred execution.
You intialize the
_items
field toThis is a query, not the answer to that query. This query is executed every time you iterate over
_items
.So in this line of your
Filter
method:the source array is enumerated and a
new Item(x)
created for each string. These items are stored in your listret
.When you call
Contains(_items.First())
after that,First()
again executes the query in_items
, creating newItem
instances for each source string.Since
Item
'sEquals
method is probably not overridden and performs a simple reference equality check, the firstItem
returned from the second iteration is a different instance ofItem
than the one in your list.