当使用Except
运算符时,我遇到了一个有趣的问题:
我有一个用户列表,想要从中排除一些用户:
用户列表来自一个XML文件:
代码实现如下:
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(users)
时,我得到了两个预期的匹配结果。问题在于当我调用users.Except(matches)
时,匹配的用户根本没有被排除!我期望得到6个用户,但是"excludes"包含了所有8个用户。由于在
GetMatchingUsers(IEnumerable<IUser> users)
中,我所做的一切只是取出与ID匹配的IUsers
(在此情况下为2个IUser
),
因此,我的理解是,默认情况下Except
将使用引用相等性来比较要排除的对象。这不是Except
的行为吗?更有趣的是,如果我使用
.ToList()
材料化对象,然后获取匹配的用户并调用Except
,一切都按预期工作!像这样:
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>
上的 Except
方法,我不明白为什么需要将对象具体化后才能调用它?
如果有任何建议/见解,将不胜感激。