est ici une question intéressante que j'ai remarqué lors de l'utilisation du Except
opérateur: Je liste des utilisateurs dont je veux exclure certains utilisateurs:LINQ Sauf opérateur et objet égalité
La liste des utilisateurs provient d'un XML fichier:
Le code va comme ceci:
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
}
}
Quand j'appelle User.GetMatchingUsers(users)
je reçois 2 matchs comme prévu. Le problème est que lorsque j'appelle users.Except(matches)
Les utilisateurs correspondants ne sont pas exclus du tout! Je m'attends à 6 utilisateurs ut "excludes" contient tous les 8 utilisateurs à la place.
Depuis tout ce que je fais dans GetMatchingUsers(IEnumerable<IUser> users)
prend les IEnumerable<IUser>
et juste retour le IUsers
dont le match ID (2 IUsers dans ce cas), je crois comprendre que, par défaut Except
utilisera l'égalité de référence pour comparer les objets être exclu. N'est-ce pas le comportement de Except
?
Ce qui est encore plus intéressant est que si je matérialise les objets à l'aide .ToList()
puis obtenir les utilisateurs correspondants, et appelle Except
, tout fonctionne comme prévu!
Comme si:
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
Je ne vois pas pourquoi je besoin de matérialiser des objets pour appeler Except
étant donné que son défini sur IEnumerable<T>
?
Toutes les suggestions/idées seraient très appréciées.
Si tel est le cas, alors les "nouveaux" objets ne seront-ils pas passés dans GetMatchingUsers à chaque fois? Cette méthode renvoie également une requête en tant que résultat et non des objets. Juste mes 2 cents ... –
Non, car l'expression est évaluée chaque fois qu'elle est utilisée. Dans mon code, qui montre cela, il est évalué par ma sortie avant l'appel à GetMatchingUsers, puis à nouveau lors de l'appel GetMatchingUSers, et surtout, encore une fois pendant l'Except. –
Étant donné que l'évaluation de GetMatchingUsers et l'exception génèrent leurs propres instances, l'exception ne fonctionne pas comme prévu. –