Aquí es un tema interesante que noté cuando se utiliza el operador Except
: tengo lista de usuarios a partir del cual quiero excluir a algunos usuarios:LINQ A excepción del operador y el objeto igualdad
La lista de usuarios está viniendo de un XML archivo:
El código dice así:
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
}
}
Cuando llamo User.GetMatchingUsers(users)
consigo 2 partidos como se esperaba. El problema es que cuando llamo al users.Except(matches)
¡Los usuarios coincidentes no se excluyen del todo! Estoy esperando 6 usuarios ut "excludes" contiene los 8 usuarios en su lugar.
Dado que todo lo que estoy haciendo en la GetMatchingUsers(IEnumerable<IUser> users)
está tomando el IEnumerable<IUser>
y que acaban de volver la IUsers
cuyo partido de Identificación (2 IUsers en este caso), mi entendimiento es que por defecto Except
utilizará la igualdad referencia para comparar los objetos de ser excluido. ¿No es así como se comporta Except
?
Lo que es aún más interesante es que si materializo los objetos utilizando .ToList()
y luego los usuarios que coinciden, y llamo Except
, todo funciona como se esperaba!
así:
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
no veo por qué debería necesitar para materializar objetos para llamar Except
dado que su definen en IEnumerable<T>
?
Cualquier sugerencia/idea sería muy apreciada.
Si ese es el caso, ¿no pasarían los objetos "nuevos" a GetMatchingUsers cada vez? Además, ese método devuelve una consulta como resultado y no como objetos. Solo mis 2 centavos ... –
No, porque la expresión se evalúa cada vez que se usa. En mi código, que muestra esto, es evaluado por mi salida antes de la llamada a GetMatchingUsers, luego cuando llamo a GetMatchingUSers, y de manera importante, nuevamente durante el Except. –
Como la evaluación de GetMatchingUsers y Except generan sus propias instancias, Except no funciona como esperaba. –