LINQ除了运营商和对象相等 [英] LINQ Except operator and object equality

查看:93
本文介绍了LINQ除了运营商和对象相等的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面是一个有趣的问题,我使用除了操作时注意到:
我有,我要排除一些用户的用户列表:

Here is an interesting issue I noticed when using the Except Operator: I have list of users from which I want to exclude some users:

用户列表是从一个XML文件来:

The list of users is coming from an XML file:









代码是这样的:

interface IUser
{
     int ID { get; set; }
     string Name { get; set; }
}

class User: IUser
{

    #region IUser Members

    public int ID
    {
        get;
        set;
    }

    public string Name
    {
        get;
        set;
    }

    #endregion

    public override string ToString()
    {
        return ID + ":" +Name;
    }


    public static IEnumerable<IUser> GetMatchingUsers(IEnumerable<IUser> users)
    {
         IEnumerable<IUser> localList = new List<User>
         {
            new User{ ID=4, Name="James"},
            new User{ ID=5, Name="Tom"}

         }.OfType<IUser>();
         var matches = from u in users
                       join lu in localList
                           on u.ID equals lu.ID
                       select u;
         return matches;
    }
}

class Program
{
    static void Main(string[] args)
    {
        XDocument doc = XDocument.Load("Users.xml");
        IEnumerable<IUser> users = doc.Element("Users").Elements("User").Select
            (u => new User
                { ID = (int)u.Attribute("id"),
                  Name = (string)u.Attribute("name")
                }
            ).OfType<IUser>();       //still a query, objects have not been materialized


        var matches = User.GetMatchingUsers(users);
        var excludes = users.Except(matches);    // excludes should contain 6 users but here it contains 8 users

    }
}

当我称之为 User.GetMatchingUsers(用户)我得到2场预期。
的问题是,当我叫 users.Except(火柴)匹配的用户不会被排除在外了! 。我期待6用户UT斯达康排除包含了所有8个用户,而不是

When I call User.GetMatchingUsers(users) I get 2 matches as expected. The issue is that when I call users.Except(matches) The matching users are not being excluded at all! I am expecting 6 users ut "excludes" contains all 8 users instead.

由于所有我做的 GetMatchingUsers(IEnumerable的< IUSER>用户)走的是的IEnumerable< IUSER> 和刚返回
中的 IUsers 其ID的比赛(2 IUsers在这种情况下),我的理解是,默认情况下除了将使用引用相等
比较的对象被排除在外。这难道不是如何除了行为?

Since all I'm doing in GetMatchingUsers(IEnumerable<IUser> users) is taking the IEnumerable<IUser> and just returning the IUsers whose ID's match( 2 IUsers in this case), my understanding is that by default Except will use reference equality for comparing the objects to be excluded. Is this not how Except behaves?

什么是更有趣的是,如果我兑现使用对象 .ToList()然后拿到匹配的用户,并调用除了,按预期工作
一切!

What is even more interesting is that if I materialize the objects using .ToList() and then get the matching users, and call Except, everything works as expected!

像这样:

IEnumerable<IUser> users = doc.Element("Users").Elements("User").Select
            (u => new User
                { ID = (int)u.Attribute("id"),
                  Name = (string)u.Attribute("name")
                }
            ).OfType<IUser>().ToList();   //explicity materializing all objects by calling ToList()

var matches = User.GetMatchingUsers(users);
var excludes = users.Except(matches);   // excludes now contains 6 users as expected



我不明白为什么我应该需要兑现对象调用除了鉴于其对中定义的IEnumerable< T>

任何suggesstions /洞察力,将不胜感激。

Any suggesstions / insights would be much appreciated.

推荐答案

我想我知道为什么这不能正常工作。由于最初的用户列表是一个LINQ的表达,这是每一个做在 GetMatchingUsers使用,并再次当它迭代(一旦时间进行了重新评估除了运行),因此,创建新的用户对象。这将导致不同的引用,因此没有匹配。使用了ToList 修复了这个,因为它只有一次迭代LINQ查询等等引用是固定的。

I think I know why this fails to work as expected. Because the initial user list is a LINQ expression, it is re-evaluated each time it is iterated (once when used in GetMatchingUsers and again when doing the Except operation) and so, new user objects are created. This would lead to different references and so no matches. Using ToList fixes this because it iterates the LINQ query once only and so the references are fixed.

我已经能够重现你已经和经调查代码中的问题,这似乎是一个非常合理的解释。我还没有证明它,虽然。

I've been able to reproduce the problem you have and having investigated the code, this seems like a very plausible explanation. I haven't proved it yet, though.

更新

我只是运行测试,但输出用户收集调用 GetMatchingUsers ,在通话,经过前。每次为对象的哈希码为输出和它们确实有不同的值,每次指示新的对象,因为我怀疑

Update
I just ran the test but outputting the users collection before the call to GetMatchingUsers, in that call, and after it. Each time the hash code for the object was output and they do indeed have different values each time indicating new objects, as I suspected.

下面是每个的输出电话:

Here is the output for each of the calls:

==> Start
ID=1, Name=Jeff, HashCode=39086322
ID=2, Name=Alastair, HashCode=36181605
ID=3, Name=Anthony, HashCode=28068188
ID=4, Name=James, HashCode=33163964
ID=5, Name=Tom, HashCode=14421545
ID=6, Name=David, HashCode=35567111
<== End
==> Start
ID=1, Name=Jeff, HashCode=65066874
ID=2, Name=Alastair, HashCode=34160229
ID=3, Name=Anthony, HashCode=63238509
ID=4, Name=James, HashCode=11679222
ID=5, Name=Tom, HashCode=35410979
ID=6, Name=David, HashCode=57416410
<== End
==> Start
ID=1, Name=Jeff, HashCode=61940669
ID=2, Name=Alastair, HashCode=15193904
ID=3, Name=Anthony, HashCode=6303833
ID=4, Name=James, HashCode=40452378
ID=5, Name=Tom, HashCode=36009496
ID=6, Name=David, HashCode=19634871
<== End

和,这里是修改后的代码,以显示该问题:

And, here is the modified code to show the problem:

using System.Xml.Linq;
using System.Collections.Generic;
using System.Linq;
using System;

interface IUser
{
    int ID
    {
        get;
        set;
    }
    string Name
    {
        get;
        set;
    }
}

class User : IUser
{

    #region IUser Members

    public int ID
    {
        get;
        set;
    }

    public string Name
    {
        get;
        set;
    }

    #endregion

    public override string ToString()
    {
        return ID + ":" + Name;
    }


    public static IEnumerable<IUser> GetMatchingUsers(IEnumerable<IUser> users)
    {
        IEnumerable<IUser> localList = new List<User>
         {
            new User{ ID=4, Name="James"},
            new User{ ID=5, Name="Tom"}

         }.OfType<IUser>();

        OutputUsers(users);
        var matches = from u in users
                      join lu in localList
                          on u.ID equals lu.ID
                      select u;
        return matches;
    }

    public static void OutputUsers(IEnumerable<IUser> users)
    {
        Console.WriteLine("==> Start");
        foreach (IUser user in users)
        {
            Console.WriteLine("ID=" + user.ID.ToString() + ", Name=" + user.Name + ", HashCode=" + user.GetHashCode().ToString());
        }
        Console.WriteLine("<== End");
    }
}

class Program
{
    static void Main(string[] args)
    {
        XDocument doc = new XDocument(
            new XElement(
                "Users",
                new XElement("User", new XAttribute("id", "1"), new XAttribute("name", "Jeff")),
                new XElement("User", new XAttribute("id", "2"), new XAttribute("name", "Alastair")),
                new XElement("User", new XAttribute("id", "3"), new XAttribute("name", "Anthony")),
                new XElement("User", new XAttribute("id", "4"), new XAttribute("name", "James")),
                new XElement("User", new XAttribute("id", "5"), new XAttribute("name", "Tom")),
                new XElement("User", new XAttribute("id", "6"), new XAttribute("name", "David"))));
        IEnumerable<IUser> users = doc.Element("Users").Elements("User").Select
            (u => new User
            {
                ID = (int)u.Attribute("id"),
                Name = (string)u.Attribute("name")
            }
            ).OfType<IUser>();       //still a query, objects have not been materialized


        User.OutputUsers(users);
        var matches = User.GetMatchingUsers(users);
        User.OutputUsers(users);
        var excludes = users.Except(matches);    // excludes should contain 6 users but here it contains 8 users

    }
}

这篇关于LINQ除了运营商和对象相等的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆