Dưới đây là một vấn đề thú vị tôi nhận thấy khi sử dụng Except
điều hành: Tôi có danh sách người dùng từ mà tôi muốn loại trừ một số người sử dụng:LINQ Trừ điều hành và đối tượng bình đẳng
Các danh sách người dùng được đến từ một XML file:
Mã này đi như thế này:
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
}
}
Khi tôi gọi User.GetMatchingUsers(users)
tôi nhận được 2 trận đấu như mong đợi. Vấn đề là khi tôi gọi users.Except(matches)
Người dùng phù hợp không bị loại trừ! Tôi hy vọng 6 người dùng ut "loại trừ" chứa tất cả 8 người dùng thay thế.
Vì tất cả Tôi đang làm trong GetMatchingUsers(IEnumerable<IUser> users)
được lấy IEnumerable<IUser>
và chỉ trở về các IUsers
có trận đấu ID (2 IUsers trong trường hợp này), sự hiểu biết của tôi là theo mặc định Except
sẽ sử dụng bình đẳng tham khảo để so sánh các đối tượng để bị loại trừ. Đây có phải là cách Except
hoạt động không?
Điều thú vị hơn nữa là nếu tôi hiện thực hóa đối tượng bằng cách sử dụng .ToList()
và sau đó nhận được người dùng phù hợp và gọi Except
, mọi thứ hoạt động như mong đợi!
Giống như vậy:
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
Tôi không thấy lý do tại sao nên tôi cần phải thực hóa các đối tượng cho gọi Except
cho rằng nó được định nghĩa trên IEnumerable<T>
?
Mọi đề xuất/thông tin chi tiết sẽ được đánh giá cao.
Nếu đúng như vậy, thì các đối tượng "mới" sẽ không được chuyển vào GetMatchingUsers mỗi lần? Ngoài ra, phương thức đó trả về một truy vấn dưới dạng kết quả và không phải là các đối tượng. Chỉ 2 xu của tôi ... –
Không, bởi vì biểu thức được đánh giá mỗi khi nó được sử dụng. Trong mã của tôi, trong đó cho thấy điều này, nó được đánh giá bởi đầu ra của tôi trước khi cuộc gọi đến GetMatchingUsers, sau đó một lần nữa khi gọi GetMatchingUSers, và quan trọng, một lần nữa trong các ngoại trừ. –
Bởi vì đánh giá cho GetMatchingUsers và ngoại trừ cả hai tạo ra trường hợp riêng của họ, ngoại trừ không làm việc như bạn mong đợi. –