LINQ除了运营商和对象相等 [英] LINQ Except operator and object equality
问题描述
下面是一个有趣的问题,我使用除了
操作时注意到:
我有,我要排除一些用户的用户列表:
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屋!