Qui è una questione interessante che ho notato quando si utilizza il Except
Operatore: ho lista di utenti con cui voglio escludere alcuni utenti:LINQ operatore Tranne ed oggetto l'uguaglianza
L'elenco degli utenti proviene da un XML File:
Il codice va in questo modo:
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
}
}
Quando chiamo User.GetMatchingUsers(users)
ottengo 2 partite come previsto. Il problema è che quando chiamo users.Except(matches)
Gli utenti corrispondenti non vengono affatto esclusi! Mi aspetto 6 utenti "esclus" contiene invece 8 utenti.
Poiché tutti che sto facendo in GetMatchingUsers(IEnumerable<IUser> users)
sta prendendo il IEnumerable<IUser>
e proprio ritorno il IUsers
cui la partita di ID (2 IUsers in questo caso), la mia comprensione è che di default Except
userà l'uguaglianza di riferimento per confrontare gli oggetti da essere escluso Non è così che si comporta Except
?
Ciò che è ancora più interessante è che se io materializzare l'oggetti utilizzando .ToList()
e quindi ottenere gli utenti corrispondenti, e chiamo Except
, tutto funziona come previsto!
Come così:
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
non vedo perché dovrei bisogno di materializzare oggetti per chiamare Except
dato che il suo definito sulla IEnumerable<T>
?
Qualsiasi suggerimento/approfondimento sarebbe molto apprezzato.
In questo caso, gli oggetti "nuovi" non verranno passati in GetMatchingUsers ogni volta? Anche questo metodo restituisce una query come risultato e non come oggetto. Solo i miei 2 centesimi ... –
No, perché l'espressione viene valutata ogni volta che viene utilizzata. Nel mio codice, che mostra questo, viene valutato dal mio output prima della chiamata a GetMatchingUsers, poi di nuovo quando si chiama GetMatchingUSers, e, soprattutto, di nuovo durante l'eccezione. –
Poiché la valutazione per GetMatchingUsers e Except generano entrambe le proprie istanze, l'eccezione non funziona come previsto. –