IEnumerable中的项目不等于List中的项目

时间:2016-12-21 15:20:59

标签: c# linq list lambda ienumerable

我无法弄清楚为什么找不到我的过滤列表中的项目。我简化了示例来展示它。我有一个班级项目......

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表达式的地点和时间以及为什么不再找到该项目有什么不同?我不明白!

3 个答案:

答案 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时,引用不再相同。因为每次创建一个新项目。你无法找到不同参考的项目。