Ответ 1
Я думаю, я знаю, почему это не работает должным образом. Поскольку первоначальный список пользователей является выражением LINQ, он пересматривается каждый раз, когда он итерируется (один раз при использовании в GetMatchingUsers
и снова при выполнении операции Except
), и поэтому создаются новые пользовательские объекты. Это приведет к разным ссылкам, и поэтому нет совпадений. Использование ToList
исправляет это, потому что оно однократно повторяет запрос LINQ, и поэтому ссылки исправлены.
Я смог воспроизвести проблему, которая у вас есть, и изучив код, это кажется очень правдоподобным объяснением. Я еще не доказал это.
Обновление
Я только что проверил тест, но вывел коллекцию users
до вызова GetMatchingUsers
в этом вызове и после него. Каждый раз, когда был выведен хэш-код для объекта, и они действительно имеют разные значения каждый раз, указывая на новые объекты, как я подозревал.
Вот результат для каждого из вызовов:
==> 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
И здесь приведенный код, чтобы показать проблему:
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
}
}