Вот интересная проблема, которую я заметил при использовании оператора 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)
, я получаю 2 матча, как ожидалось.
Проблема в том, что когда я вызываю users.Except(matches)
Совпадающие пользователи вообще не исключаются! Я ожидаю, что 6 пользователей ut "исключает" содержит всего 8 пользователей.
Поскольку все, что я делаю в GetMatchingUsers(IEnumerable<IUser> users)
, принимает IEnumerable<IUser>
и просто возвращается
IUsers
, чье совпадение ID (в этом случае два IUsers), я понимаю, что по умолчанию 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
Я не понимаю, зачем мне нужно материализовывать объекты для вызова Except
, учитывая, что он определен на IEnumerable<T>
?
Любые предложения/идеи будут высоко оценены.