Tôi luôn luôn giả định rằng nếu tôi đang sử dụng Select(x=> ...)
trong ngữ cảnh LINQ cho đối tượng, thì bộ sưu tập mới sẽ được tạo ngay lập tức và giữ nguyên. Tôi không hoàn toàn chắc chắn TẠI SAO tôi giả định điều này, và nó là một giả định rất xấu nhưng tôi đã làm. Tôi thường sử dụng .ToList()
ở nơi khác, nhưng thường không phải trong trường hợp này.Làm cách nào để biết liệu một số IE2umercó thể thực hiện được hoãn lại không?
Mã này chứng minh rằng ngay cả một đơn giản 'Chọn' là tùy thuộc vào thực hiện chậm:
var random = new Random();
var animals = new[] { "cat", "dog", "mouse" };
var randomNumberOfAnimals = animals.Select(x => Math.Floor(random.NextDouble() * 100) + " " + x + "s");
foreach (var i in randomNumberOfAnimals)
{
testContextInstance.WriteLine("There are " + i);
}
foreach (var i in randomNumberOfAnimals)
{
testContextInstance.WriteLine("And now, there are " + i);
}
này kết quả đầu ra như sau (các chức năng ngẫu nhiên được gọi mỗi khi bộ sưu tập được lặp thông qua):
There are 75 cats
There are 28 dogs
There are 62 mouses
And now, there are 78 cats
And now, there are 69 dogs
And now, there are 43 mouses
Tôi có nhiều nơi tôi có số IEnumerable<T>
làm thành viên của một lớp học. Thường thì kết quả của truy vấn LINQ được gán cho một số IEnumerable<T>
. Thông thường đối với tôi điều này không gây ra vấn đề, nhưng gần đây tôi đã tìm thấy một vài nơi trong mã của tôi, nơi nó đặt ra nhiều hơn là một vấn đề hiệu suất.
Khi cố gắng kiểm tra những nơi mà tôi đã mắc phải sai lầm này, tôi nghĩ tôi có thể kiểm tra xem một số IEnumerable<T>
cụ thể có thuộc loại IQueryable
hay không. Điều này tôi nghĩ sẽ cho tôi biết nếu bộ sưu tập được 'trì hoãn' hay không. Nó chỉ ra rằng điều tra viên tạo ra bởi các nhà điều hành lựa chọn ở trên là loại System.Linq.Enumerable+WhereSelectArrayIterator``[System.String,System.String]
và không IQueryable
.
Tôi đã sử dụng Reflector để xem giao diện này được kế thừa từ đâu và hóa ra không kế thừa từ bất kỳ thứ gì cho biết đó là 'LINQ' - vì vậy không có cách nào để kiểm tra dựa trên loại bộ sưu tập.
Tôi khá vui khi đặt .ToArray()
ở mọi nơi, nhưng tôi muốn có một cơ chế để đảm bảo vấn đề này không xảy ra trong tương lai. Visual Studio dường như biết làm thế nào để làm điều đó bởi vì nó đưa ra một thông báo về 'mở rộng xem kết quả sẽ đánh giá bộ sưu tập.'
Điều tốt nhất tôi đã đưa ra là:
bool deferred = !object.ReferenceEquals(randomNumberOfAnimals.First(),
randomNumberOfAnimals.First());
Edit: chỉ này hoạt động nếu một đối tượng mới được tạo ra với 'Chọn' và nó không phải là một giải pháp chung. Tôi không khuyến khích nó trong mọi trường hợp mặc dù! Đó là một lưỡi nhỏ trong má của một giải pháp.
Hãy nhớ rằng, biểu thức truy vấn cung cấp cho bạn một đối tượng đại diện cho * chính truy vấn *. Đối tượng không đại diện cho các kết quả của truy vấn, đối tượng đại diện cho một truy vấn. Hãy coi nó như một chuỗi truy vấn SQL, chỉ thông minh hơn. Bạn yêu cầu truy vấn cho kết quả của nó và truy vấn thực thi. Bạn hỏi lại lần nữa, truy vấn sẽ thực thi lại; không đảm bảo rằng kết quả sẽ giống như lần thứ hai bạn hỏi; thế giới có thể đã thay đổi kể từ đó. –
Điều tốt nhất tôi đã gặp phải là [ở đây] (http://stackoverflow.com/questions/4621561/is-there-a-way-to-memorize-or-materialize-an-ienumerable), không dễ dàng mặc dù .. ! – nawfal
Tại sao bạn quan tâm đến việc thực thi hoãn lại? Bạn không nên chú ý đến điều đó, và coi nó là một chi tiết thực hiện riêng tư của bạn 'IEnumerable '. –