2008-12-19 27 views
28

Có vấn đề với việc sử dụng IEnumerable<T> làm kiểu trả về không? FxCop than phiền về việc trả lại List<T> (thay vào đó, bạn nên trả lại Collection<T>).IEnumerable <T> là kiểu trả lại

Vâng, tôi luôn được hướng dẫn bởi một quy tắc "chấp nhận ít nhất bạn có thể, nhưng trả lại số tiền tối đa".

Từ quan điểm này, trả lại IEnumerable<T> là một điều xấu, nhưng tôi nên làm gì khi tôi muốn sử dụng "truy xuất chậm"? Ngoài ra, các từ khóa yield là một goodie.

Trả lời

27

Đây thực sự là câu hỏi hai phần.

1) Có vốn điều gì sai trái với trả lại một IEnumerable <T>

Không gì cả. Trong thực tế, nếu bạn đang sử dụng C# iterators thì đây là hành vi mong đợi. Việc chuyển đổi nó thành một Danh sách <T> hoặc một lớp thu gom khác không phải là một ý tưởng hay. Làm như vậy là làm cho một giả định về mẫu sử dụng bởi người gọi của bạn. Tôi tìm thấy nó không phải là một ý tưởng tốt để giả định bất cứ điều gì về người gọi. Họ có thể có lý do chính đáng tại sao họ muốn có một số điện thoại <T>. Có lẽ họ muốn chuyển đổi nó sang một hệ thống phân cấp bộ sưu tập hoàn toàn khác (trong trường hợp đó một chuyển đổi thành Danh sách bị lãng phí).

2) Có bất kỳ trường hợp nào có thể thích hợp hơn để trả lại một số thứ khác ngoài IEnumerable <T>?

Có. Mặc dù không phải là một ý tưởng tuyệt vời để giả định nhiều về người gọi của bạn, hoàn toàn ổn định để đưa ra quyết định dựa trên hành vi của riêng bạn. Hãy tưởng tượng một kịch bản mà bạn đã có một đối tượng đa luồng đang xếp hàng các yêu cầu vào một đối tượng liên tục được cập nhật.Trong trường hợp này, trả lại một số IE2umeric <T> là vô trách nhiệm. Ngay sau khi bộ sưu tập được sửa đổi, số đếm được vô hiệu và sẽ làm cho việc thực thi xảy ra. Thay vào đó, bạn có thể chụp nhanh cấu trúc và trả về giá trị đó. Nói trong một mẫu <T>. Trong trường hợp này tôi sẽ chỉ trả về đối tượng là cấu trúc trực tiếp (hoặc giao diện).

Đây chắc chắn là trường hợp hiếm hơn.

+0

Như OP chỉ ra, nếu một lớp cụ thể cần được trả về, FxCop khuyên trả về một 'Bộ sưu tập ' thay vì 'Danh sách '. Ngoài ra, hãy xem [Bộ sưu tập so với Danh sách bạn nên sử dụng những gì trên giao diện của mình?] (http://stackoverflow.com/q/271710/1497596). – DavidRR

27

Không, IEnumerable<T>tốt điều cần trả lại ở đây, vì tất cả những gì bạn hứa hẹn là "một chuỗi các giá trị (đã nhập)". Lý tưởng cho LINQ vv, và hoàn toàn có thể sử dụng.

Người gọi có thể dễ dàng đưa dữ liệu này vào danh sách (hoặc bất kỳ thứ gì) - đặc biệt là với LINQ (ToList, ToArray, v.v ...).

Cách tiếp cận này cho phép bạn lazily spool giá trị trở lại, thay vì phải đệm tất cả dữ liệu. Chắc chắn là một goodie. Tôi cũng đã viết another useful IEnumerable<T> trick vào một ngày khác.

+1

bất kỳ suy nghĩ nào về lý do tại sao fxcop than phiền? – annakata

+0

Google có khiếu nại về việc trả lại số điện thoại của IEn không? Nếu vậy, lấy nó với một chút muối ... nó không phải lúc nào cũng đúng. –

+3

Nếu nó nhắc bạn suy nghĩ về những gì bạn * thực sự * muốn quay trở lại, fxcop đã thực hiện công việc của mình ;-p –

4

IE có thể bị phạt bởi tôi nhưng có một số hạn chế. Khách hàng phải liệt kê để có được kết quả. Nó không có cách nào để kiểm tra Đếm vv. Danh sách là xấu bởi vì bạn tiếp xúc với nhiều kiểm soát; khách hàng có thể thêm/xóa vv từ đó và đó có thể là một điều xấu. Bộ sưu tập có vẻ là thỏa hiệp tốt nhất, ít nhất là trong quan điểm của FxCop. Tôi allways sử dụng những gì có vẻ appropiate trong bối cảnh của tôi (ví dụ: nếu tôi muốn trả lại một bộ sưu tập chỉ đọc tôi phơi bày bộ sưu tập như là loại trở lại và trở lại List.AsReadOnly() hoặc IEnumerable cho lười biếng đánh giá thông qua sản lượng vv). Hãy xem xét từng trường hợp theo từng trường hợp

+1

Khách hàng có thể sử dụng ToList(), ToArray(), Count() hoặc bất cứ điều gì nó muốn. –

+1

nhưng những phương pháp đó tạo đối tượng mới. bất kỳ sửa đổi nào được thực hiện không phản ánh trên đối tượng danh sách gốc –

+0

Điểm tốt! cơ sở từng trường hợp có vẻ tốt nhất. Mặc dù tôi thích cách tiếp cận IEnumerable, nó ủng hộ bất biến. –

1

Chỉ vì bạn nói bạn đang trả về IEnumerable không có nghĩa là bạn không thể trả lại Danh sách. Ý tưởng là giảm ghép nối không cần thiết. Tất cả những gì người gọi cần quan tâm là nhận được một danh sách những thứ, thay vì loại chính xác của bộ sưu tập được sử dụng để chứa danh sách đó. Nếu bạn có một cái gì đó được hỗ trợ bởi một mảng, sau đó nhận được một cái gì đó như Count là sẽ được nhanh anyway.

+3

Nếu người gọi mong đợi một * danh sách *, thì IList [] sẽ là một lựa chọn tốt. –

0

Tôi nghĩ rằng hướng dẫn của riêng bạn là tuyệt vời - nếu bạn có thể cụ thể hơn về những gì bạn đang quay trở lại mà không đạt được hiệu suất (bạn không cần phải tạo Danh sách ngoài kết quả của mình) . Nhưng nếu chức năng của bạn hợp pháp không biết loại nó sẽ tìm thấy, như trong một số trường hợp bạn sẽ làm việc với một Danh sách và trong một số với một mảng, vv, sau đó trở về IEnumerable là "tốt nhất" bạn có thể làm . Hãy suy nghĩ về nó như là "đa phổ biến nhất" của tất cả mọi thứ bạn có thể muốn quay trở lại.

2

Trả về IEnumerable <T> là OK nếu bạn thực sự chỉ trả về một điều tra và nó sẽ được người gọi của bạn tiêu thụ như vậy.

Nhưng như những người khác chỉ ra, nó có nhược điểm mà người gọi có thể cần phải liệt kê nếu anh ta cần bất kỳ thông tin nào khác (ví dụ: Đếm). Phương thức mở rộng .NET 3.5 IEnumerable <T> .Count sẽ liệt kê đằng sau hậu trường nếu giá trị trả về không thực hiện ICollection <T>, điều này có thể không mong muốn.

tôi thường trở về IList <T> hoặc ICollection <T> khi kết quả là một bộ sưu tập - nội phương pháp bạn có thể sử dụng một danh sách <T> và hoặc trả lại như nó vốn có, hoặc trả lại Danh sách <T> .AsReadOnly nếu bạn muốn bảo vệ chống lại sửa đổi (ví dụ: nếu bạn đang lưu vào bộ nhớ cache danh sách nội bộ). AFAIK FxCop khá hài lòng với một trong những điều này.

3

Về nguyên tắc của bạn: "chấp nhận ít nhất bạn có thể, nhưng trả lại giá trị tối đa".

Chìa khóa để quản lý sự phức tạp của một chương trình lớn là kỹ thuật được gọi là thông tin ẩn. Nếu phương pháp của bạn hoạt động bằng cách xây dựng một List<T>, thường không cần thiết phải tiết lộ sự kiện này bằng cách trả về loại đó. Nếu bạn làm vậy, người gọi của bạn có thể sửa đổi danh sách họ nhận được. Điều này sẽ xóa khả năng của bạn để lưu vào bộ nhớ đệm hoặc lặp lại lười biếng với yield return.

Vì vậy, nguyên tắc tốt hơn là cho một chức năng để làm theo là: "tiết lộ càng ít càng tốt về cách bạn làm việc".

2

Một khía cạnh quan trọng là khi bạn trả lại List<T>, bạn thực tế trả về một tài liệu tham khảo . Điều đó giúp người gọi có thể thao túng danh sách của bạn. Đây là một vấn đề phổ biến - ví dụ, một lớp nghiệp vụ trả về một List<T> cho một lớp GUI.

3

"chấp nhận ít nhất bạn có thể, nhưng trả lại tối đa" là những gì tôi ủng hộ. Khi một phương thức trả về một đối tượng, chúng ta sẽ không trả về loại thực tế và giới hạn các khả năng của đối tượng bằng cách trả về một kiểu cơ sở. Điều này tuy nhiên đặt ra một câu hỏi làm thế nào để chúng ta biết những gì "tối đa" (loại thực tế) sẽ được khi chúng tôi thiết kế một giao diện. Câu trả lời rất đơn giản. Chỉ trong trường hợp cực đoan mà người thiết kế giao diện đang thiết kế một giao diện mở, sẽ được thực hiện bên ngoài ứng dụng/thành phần, họ sẽ không biết loại trả về thực tế có thể là gì. Một nhà thiết kế thông minh nên luôn luôn xem xét những gì phương pháp nên được làm và những gì một loại trả về tối ưu/chung chung nên được.

Ví dụ: Nếu tôi đang thiết kế một giao diện để lấy ra một vectơ các đối tượng, và tôi biết số lượng các đối tượng được trả về sẽ biến đổi, tôi sẽ luôn luôn giả sử một nhà phát triển thông minh sẽ luôn sử dụng một Danh sách. Nếu ai đó có kế hoạch trả lại một mảng, tôi sẽ đặt câu hỏi về khả năng của anh ta, trừ khi anh ấy/cô ấy chỉ trả lại dữ liệu từ một lớp khác mà anh ta/cô ấy không sở hữu. Và đây có lẽ là lý do tại sao FxCop ủng hộ ICollection (cơ sở chung cho Danh sách và Mảng).

Trên đây đang được nói, có vài thứ khác để xem xét

  • nếu dữ liệu trả về nên có thể thay đổi hoặc không thay đổi

  • nếu dữ liệu trả về được chia sẻ trên nhiều người gọi

Liên quan đến đánh giá lười biếng LINQ Tôi chắc chắn 95% người dùng C# không hiểu thông tin. Nó không phải như vậy. OO thúc đẩy các thay đổi trạng thái cụ thể về các lời gọi phương thức. LINQ lười biếng đánh giá thúc đẩy thay đổi trạng thái thời gian chạy trên mô hình đánh giá biểu hiện (không phải là một cái gì đó không cao cấp người dùng luôn luôn làm theo).

0

Tôi không thể chấp nhận câu trả lời đã chọn. Có nhiều cách để xử lý kịch bản được mô tả nhưng sử dụng Danh sách hoặc bất kỳ thứ gì khác mà bạn sử dụng không phải là một trong số chúng. Thời điểm IEnumerable được trả về bạn phải giả định rằng người gọi có thể làm một foreach. Trong trường hợp đó, nó không quan trọng nếu loại bê tông là List hoặc spaghetti. Trong thực tế chỉ cần lập chỉ mục là một vấn đề đặc biệt là nếu các mục được gỡ bỏ.

Bất kỳ giá trị trả lại nào là ảnh chụp nhanh. Nó có thể là nội dung hiện tại của IEnumerable trong trường hợp nếu nó được lưu trữ, nó phải là một bản sao của bản sao được lưu trong bộ nhớ cache; nếu nó được cho là năng động hơn (như resuts của một truy vấn sql) thì sử dụng lợi nhuận; tuy nhiên cho phép container thay đổi theo ý muốn và cung cấp các phương thức như Count và indexer là một công thức cho thảm họa trong một thế giới đa luồng. Tôi thậm chí không nhận được vào khả năng của người gọi để gọi Thêm hoặc Xóa trên một container chứa mã của bạn được cho là để kiểm soát.

Cũng trả về loại bê tông sẽ khóa bạn vào triển khai. Hôm nay nội bộ bạn có thể đang sử dụng một danh sách. Ngày mai có thể bạn trở nên đa luồng và muốn sử dụng một thùng chứa an toàn chủ đề hoặc một mảng hoặc một hàng đợi hoặc bộ sưu tập Giá trị của từ điển hoặc đầu ra của truy vấn LINQ. Nếu bạn tự khóa mình vào loại trả về cụ thể thì bạn phải thay đổi một loạt mã hoặc thực hiện chuyển đổi trước khi trở về.

Các vấn đề liên quan