Tại sao tôi nên sử dụng Danh sách <T> trên IEnumerable <T>?


24

Trong ứng dụng web ASP4 MVC4 của tôi, tôi sử dụng IEnumerables, cố gắng tuân theo câu thần chú để lập trình lên giao diện chứ không phải thực hiện.

Return IEnumerable(Of Student)

đấu với

Return New List(Of Student)

Mọi người đang bảo tôi sử dụng Danh sách chứ không phải IEnumerable, vì các danh sách buộc truy vấn phải được thực thi và IEumerable thì không.

Đây thực sự là thực hành tốt nhất? Có sự thay thế nào không? Tôi cảm thấy kỳ lạ khi sử dụng các đối tượng cụ thể nơi giao diện có thể được sử dụng. Là cảm giác kỳ lạ của tôi là hợp lý?


2
Er, đầu tiên, tại sao nó là một điều tốt để buộc truy vấn được thực hiện? Thứ hai, bạn nên theo dõi và định hình các cuộc gọi cơ sở dữ liệu để đánh giá xem việc xem xét kỹ thuật này có bất kỳ giá trị nào không.
dùng16764

2
Người ta nói rằng tất cả các truy vấn nên được thực hiện để mô hình được thực hiện và được tải sẵn sàng để xem. Tức là khung nhìn sẽ nhận được mọi thứ và không được truy vấn cơ sở dữ liệu.
Rowan Freeman

3
Đây câu hỏi StackOverflow bao gồm nó khá tốt.
Karl Bielefeldt

Đó là một câu trả lời hay, nhưng tôi muốn biết nó có liên quan đến MVC. Tại sao chế độ xem không thể được cung cấp cho IEnumerables để tất cả các truy vấn chạy đúng lúc?
Rowan Freeman

2
"Người ta nói rằng tất cả các truy vấn nên được thực hiện để mô hình được thực hiện và được tải sẵn sàng cho chế độ xem. Tức là chế độ xem sẽ nhận được mọi thứ và không được truy vấn cơ sở dữ liệu." Điều đó thật vớ vẩn. Nếu bạn vượt qua IEnumerable, thì chế độ xem của bạn sẽ không biết hoặc không quan tâm liệu nó có truy vấn cơ sở dữ liệu hay không. Và đó là cách nó phải như vậy.
dùng16764

Câu trả lời:


21

Đôi khi việc thực hiện một ToList()truy vấn linq của bạn có thể rất quan trọng để đảm bảo các truy vấn của bạn được thực thi tại thời điểm và theo thứ tự mà bạn mong đợi. Tuy nhiên, những kịch bản đó rất hiếm và không có gì phải lo lắng quá nhiều cho đến khi chúng thực sự chạy vào chúng.

Câu chuyện dài, sử dụng IEnumerablebất cứ lúc nào bạn chỉ cần lặp, sử dụng IListkhi bạn cần lập chỉ mục trực tiếp và cần một mảng có kích thước động (nếu bạn cần lập chỉ mục trên một mảng có kích thước cố định thì chỉ cần sử dụng một mảng tiêu chuẩn).

Đối với các điều thời gian thực hiện, bạn luôn có thể sử dụng một danh sách như một IEnumerablebiến, vì vậy cảm thấy tự do để trả lại một IEnumerablebằng cách thực hiện một .ToList();, hoặc vượt qua trong một tham số như một IEnumerablebằng cách thực hiện .ToList()trên IEnumerableđể lực lượng thực hiện ngay sau đó và ở đó. Hãy cẩn thận rằng bất cứ khi nào bạn buộc thực thi với .ToList()bạn, đừng bám vào IEnumerablebiến mà bạn vừa thực hiện và thực hiện lại, nếu không bạn sẽ tăng gấp đôi số lần lặp trong truy vấn LINQ của mình một cách không cần thiết.

Liên quan đến MVC, thực sự không có gì đặc biệt cần lưu ý ở đây. Nó sẽ tuân theo các quy tắc thời gian thực hiện giống như phần còn lại của .NET, tôi nghĩ rằng bạn có thể có ai đó bị nhầm lẫn do ngữ nghĩa thực thi bị trì hoãn trong quá khứ và đổ lỗi cho MVC cho bạn biết điều này có liên quan, nhưng nó không phải. Các ngữ nghĩa thực thi bị trì hoãn gây nhầm lẫn cho tất cả mọi người lúc đầu (và thậm chí trong một thời gian sau đó; chúng có thể là một mánh khóe cảm ứng). Mặc dù vậy, một lần nữa, đừng lo lắng về điều đó cho đến khi bạn thực sự quan tâm đến việc đảm bảo truy vấn LINQ không được thực hiện hai lần hoặc yêu cầu nó được thực hiện theo một thứ tự nhất định so với mã khác, tại thời điểm đó, hãy gán biến của bạn cho chính nó. ToList () để thực thi và bạn sẽ ổn thôi.


Có phải là xấu khi đưa ra một quan điểm IEnumerables? Bạn có nên cung cấp cho nó Danh sách để các truy vấn đã được thực hiện tại thời điểm chế độ xem nhận được chúng không?
Rowan Freeman

@RowanFreeman Tôi vừa thêm một chỉnh sửa để trả lời điều này. Tôi nghĩ rằng bạn đã có ai đó gặp phải điều gì đó mà họ không hoàn toàn hiểu được (không thể đổ lỗi cho họ, việc thực thi bị trì hoãn là rất phức tạp và khó hiểu) và đã biến nó thành joojoo xấu thay vì làm việc để hiểu toàn bộ hành vi của việc thực hiện bị trì hoãn ngữ nghĩa.
Jimmy Hoffa

Câu trả lời tốt đẹp. Vì vậy, tôi sẽ thực sự cần phải sử dụng .ToList ()? Cho đến nay, ứng dụng của tôi hoạt động tốt khi chỉ sử dụng IEnumerables và chuyển chúng từ mô hình sang dạng xem. Không có danh sách hoặc .ToList (). Câu hỏi của tôi không phải là một trong các chức năng - Tôi biết ứng dụng của tôi hoạt động. Câu hỏi của tôi là một trong những thực hành tốt nhất.
Rowan Freeman

4
@RowanFreeman thực hành tốt nhất là sử dụng giao diện tối thiểu vẫn đáp ứng yêu cầu của bạn. IEnumerable đang làm điều này ngay bây giờ cho bạn vì vậy đừng lo lắng về việc thay đổi nó. Điều đó nói rằng, một ngày sẽ đến khi bạn có một truy vấn thực thi 3 hoặc 10 lần và không hiểu tại sao, hoặc mong đợi một truy vấn được thực thi trước khi chèn chỉ để tìm thấy nó thực thi sau đó, đây là những lần bạn cần nhận ra .ToList () sẽ buộc thực thi khi bạn muốn và nhắc lại một IEnumerable từ truy vấn LINQ nhiều lần sẽ thực thi toàn bộ truy vấn nhiều lần; Khắc phục sự thực thi của bạn khi những sự kiện đó xảy ra
Jimmy Hoffa

1
Bạn thậm chí không nên sử dụng - Listvượt qua một Listhàm ý rằng nội dung của danh sách sẽ được sửa đổi. Nếu bạn muốn trả lại một bộ sưu tập, sử dụng IReadOnlyCollection. Listlà để sử dụng trong các phương thức và để trao đổi giữa các phương thức sửa đổi danh sách. Đó là nó!
ErikE

7

Có hai vấn đề.

IENumerable<Data> query = MyQuery();

//Later
foreach (Data item in query) {
  //Process data
}

Vào thời điểm vòng lặp "Dữ liệu quá trình" đạt được, truy vấn có thể không còn hợp lệ. Ví dụ: nếu truy vấn đang được chạy trên DataContext đã bị loại bỏ, mã của bạn sẽ đưa ra một ngoại lệ. Loại điều này trở nên rất khó hiểu khi bạn đang xử lý một truy vấn trong một ngữ cảnh khác với nơi bạn đã tạo nó.

Một vấn đề thứ yếu là kết nối của bạn sẽ không được phát hành cho đến khi vòng lặp "Dữ liệu quá trình" hoàn tất. Đây chỉ là một vấn đề nếu "Dữ liệu quá trình" phức tạp. Điều này được đề cập tại http://msdn.microsoft.com/en-us/l Library / bb386929.aspx :

Q. Bao lâu kết nối cơ sở dữ liệu của tôi vẫn mở?

A. Một kết nối thường vẫn mở cho đến khi bạn sử dụng kết quả truy vấn. Nếu bạn muốn mất thời gian để xử lý tất cả các kết quả và không phản đối việc lưu trữ kết quả, hãy áp dụng ToList cho truy vấn. Trong các kịch bản phổ biến trong đó mỗi đối tượng chỉ được xử lý một lần, mô hình phát trực tuyến vượt trội hơn về cả DataReader và LINQ to SQL.

Vì vậy, những vấn đề này là lý do tại sao bạn được khuyến khích để đảm bảo rằng truy vấn thực sự được thực thi, ví dụ như bằng cách gọi ToList(). Tuy nhiên, như Jimmy Gợi ý , không có gì ngăn bạn trả lại Danh sách của bạn dưới dạng IEnumerable.

Theo nguyên tắc chung, tôi khuyên bạn nên tránh lặp lại nhiều lần hơn một lần. Giả sử người tiêu dùng mã của bạn tuân theo quy tắc này, tôi không coi đó là mối lo ngại rằng ai đó có thể truy cập cơ sở dữ liệu hai lần bằng cách thực hiện truy vấn hai lần.


1

Một lợi ích khác của việc liệt kê IEnumerablesớm là các ngoại lệ sẽ được ném vào vị trí thích hợp. Điều này hỗ trợ gỡ lỗi.

Ví dụ: nếu bạn có một ngoại lệ bế tắc bên trong một trong các chế độ xem Dao cạo của mình, thì nó sẽ không thực sự rõ ràng như thể ngoại lệ xảy ra trong một trong các phương thức truy cập dữ liệu của bạn.


Bất kỳ phương pháp nào trả về một IEnumerablecái có thể ném có thể là một sai lầm. Các IEnumerablephương thức hoãn lại nên được chia thành hai: một phương thức không hoãn lại kiểm tra các tham số và thiết lập, ném nếu cần thiết (giả sử, vì một đối số null). Sau đó, nó trả về một cuộc gọi đến việc thực hiện riêng tư bị hoãn lại. Tôi không nghĩ rằng nhận xét của tôi hoàn toàn mâu thuẫn với câu trả lời của bạn, nhưng tôi nghĩ rằng bạn đã bỏ qua một khía cạnh quan trọng trong câu trả lời của bạn, đó là ý nghĩa ngữ nghĩa của việc sử dụng IEnumerableso với List(đột biến) so với IReadOnlyCollection(không có lợi cho trì hoãn) .
ErikE
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.