Вот интересный вопрос, который я заметил при использовании Except
Оператора: У меня есть список пользователей, из которых я хочу, чтобы исключить некоторые пользователей:LINQ Кроме оператор и объект равенства
Список пользователей приходят из 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
, чей матч удостоверения личности (2 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>
?
Любые предложения или идеи были бы высоко оценены.
Если это так, то не будут ли «новые» объекты передаваться в GetMatchingUsers каждый раз? Также этот метод возвращает запрос как результат, а не объекты. Только мои 2 цента ... –
Нет, потому что выражение оценивается каждый раз, когда оно используется. В моем коде, который показывает это, он оценивается моим выходом до вызова GetMatchingUsers, затем снова при вызове GetMatchingUSers и, что еще важнее, снова во время Except. –
Поскольку оценка для GetMatchingUsers и Exception генерирует их собственные экземпляры, Except не работает так, как вы ожидаете. –