Có phải là không hợp lý khi mong đợi Any () * không * ném ngoại lệ tham chiếu null?


41

Khi bạn tạo ra một phương pháp mở rộng bạn có thể, tất nhiên, gọi nó là trên null.Nhưng, không giống như một cuộc gọi phương pháp dụ, gọi đó là trên null không để ném một NullReferenceException-> bạn phải kiểm tra và ném nó bằng tay.

Để thực hiện phương thức mở rộng Linq, Any()Microsoft đã quyết định rằng họ nên ném một ArgumentNullException( https://github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/Linq/AnyAll.cs ).

Nó làm tôi khó chịu khi phải viết if( myCollection != null && myCollection.Any() )

Tôi có sai không, với tư cách là khách hàng của mã này, để mong rằng ví dụ đó ((int[])null).Any()sẽ quay trở lại false?


41
trong C # 6, bạn có thể đơn giản hóa séc của mình thành if (myCollection? .Any () == true)
17 của 26 tháng

5
Ngay cả trong F #, không thực sự sử dụng null ngoại trừ khả năng tương tác với các ngôn ngữ .NET khác, null |> Seq.isEmptysẽ ném System.ArgumentNullException: Value cannot be null. Kỳ vọng dường như là bạn sẽ không chuyển các giá trị không xác định cho thứ gì đó được dự kiến ​​tồn tại, do đó, đây vẫn là một ngoại lệ khi bạn có giá trị rỗng. Nếu đó là vấn đề khởi tạo, tôi sẽ bắt đầu với một chuỗi trống thay vì a null.
Aaron M. Eshbach

21
Đó là lý do tại sao bạn không nên quay lại nullkhi xử lý các bộ sưu tập mà thay vào đó hãy sử dụng một bộ sưu tập trống trong các trường hợp đó.
Bakuriu

31
Tại sao nó là null ở nơi đầu tiên? Bạn không muốn null được tính là trống, bởi vì nó không trống, nó hoàn toàn khác. Có thể bạn muốn nó được tính là trống trong trường hợp của bạn, nhưng việc thêm loại điều đó vào ngôn ngữ sẽ dẫn đến lỗi.
dùng253751

22
Tôi nên chỉ ra rằng mọi phương thức LINQ đều ném vào một đối số null. Anychỉ là nhất quán.
Sebastian Redl

Câu trả lời:


157

Tôi có một túi với năm củ khoai tây trong đó. Có .Any()khoai tây trong túi?

" Vâng ," bạn nói.<= true

Tôi lấy tất cả khoai tây ra và ăn chúng. Có .Any()khoai tây trong túi?

" Không ," bạn nói.<= false

Tôi thiêu rụi hoàn toàn cái túi trong lửa. Có .Any()khoai tây trong túi bây giờ?

" Không có túi ."<= ArgumentNullException


3
Nhưng bỏ trống, không có khoai tây trong túi? Sai ngụ ý Sai ... (Tôi không đồng ý, chỉ là góc nhìn thứ hai).
D. Ben Knoble

6
thực tế hơn, ai đó đưa cho bạn một hộp kín từ một nguồn không xác định. Cái túi trong hộp có chứa khoai tây không? Tôi chỉ quan tâm đến 2 kịch bản - A) có một túi trong hộp có khoai tây trong đó, hoặc B) không có khoai tây và / hoặc không có túi trong hộp. Về mặt ngữ nghĩa, vâng, có một sự khác biệt giữa null và rỗng, nhưng 99% thời gian tôi không quan tâm.
dave thieben

6
Bạn đã ăn năm củ khoai tây sống ?? Tìm kiếm sự chăm sóc y tế ngay lập tức.
Oly

3
@Oly Dan đã nấu chúng trong lửa, trước khi ăn và dùng lửa đó để đốt túi.
Ismael Miguel

3
@ D.BenKnoble: Không nhìn thấy cốp xe của tôi, bạn có sẵn sàng làm chứng rằng không có thi thể nào trong cốp xe của tôi không? Không? Có gì khác nhau giữa việc kiểm tra thân cây và không tìm thấy gì, hoặc không kiểm tra thân cây? Bây giờ bạn không thể làm chứng, vì bạn đã nhìn thấy cùng một lượng cơ thể trong cả hai trường hợp? ... Đó là sự khác biệt. Bạn không thể bảo đảm cho một cái gì đó nếu bạn không thể thực sự xác nhận nó ở nơi đầu tiên. Bạn cho rằng hai cái này là tương đương nhau, nhưng đó không phải là nhất định. Trong một số trường hợp, có thể có một sự khác biệt quan trọng giữa hai.
Flater

52

Trước hết, có vẻ như mã nguồn sẽ ném ArgumentNullException, không NullReferenceException.

Phải nói rằng, trong nhiều trường hợp bạn đã biết rằng bộ sưu tập của bạn không phải là null, bởi vì mã này chỉ được gọi từ mã biết rằng bộ sưu tập đã tồn tại, vì vậy bạn sẽ không phải thường xuyên kiểm tra null. Nhưng nếu bạn không biết rằng nó tồn tại, thì nên kiểm tra trước khi gọi Any()nó.

Tôi có sai không, với tư cách là khách hàng của mã này, để mong rằng ví dụ đó ((int[])null).Any()sẽ quay trở lại false?

Đúng. Câu hỏi mà Any()câu trả lời là "bộ sưu tập này có chứa yếu tố nào không?" Nếu bộ sưu tập này không tồn tại, thì bản thân câu hỏi là vô nghĩa; nó không thể chứa hay không chứa bất cứ thứ gì, vì nó không tồn tại.


18
@ChrisWohlert: Trực giác của bạn hấp dẫn tôi. Bạn có nghĩ rằng về mặt ngữ nghĩa, một người không tồn tại là một người trống rỗng, tên của họ là chuỗi rỗng và tuổi là 0 và cứ thế? Và bạn có nghĩ rằng một tập hợp chứa nulltương đương với một tập hợp chứa tập hợp trống (và ngược lại) không?
ruakh

17
@ChrisWohlert: Rõ ràng một bộ có thể chứa bộ trống; nhưng bạn dường như tin rằng { null }{ {} }nên tương đương. Tôi thấy điều đó thật hấp dẫn; Tôi không thể liên quan đến nó cả.
ruakh

9
Có nên .Addtrên một nullbộ sưu tập bằng cách nào đó tạo ra một bộ sưu tập cho chúng ta quá?
Matthew

13
@ChrisWohlert "A là một bộ trống. B là một bộ chứa dưa hấu. Có phải là trống không? Có. B có trống không? Không. C có trống không? Uhhh ..."
user253751

16
Điểm phạm vi: Không phải là trường hợp, trong lý thuyết tập hợp, tập không tồn tại là tập rỗng. Tập hợp trống tồn tại và bất kỳ tập hợp không tồn tại nào không phải là nó (và thực tế, không có gì cả, vì nó không tồn tại).
James_pic

22

Null có nghĩa là thiếu thông tin, không phải không có yếu tố.

Bạn có thể xem xét rộng hơn để tránh null - ví dụ: sử dụng một trong các bảng liệt kê trống tích hợp để thể hiện một bộ sưu tập không có phần tử thay vì null.

Nếu bạn trả về null trong một số trường hợp, bạn có thể thay đổi điều đó để trả về bộ sưu tập trống. (Mặt khác, nếu bạn tìm thấy null được trả về bởi các phương thức thư viện (không phải của bạn), điều đó thật đáng tiếc, và tôi sẽ bọc chúng để bình thường hóa.)

Xem thêm https://stackoverflow.com/questions/1191919/what-does-linq-return-when-the-results-are-empty


1
Điều đó nullkhông có nghĩa là không có yếu tố nào là vấn đề sử dụng quy ước hợp lý. Chỉ cần nghĩ về OLESTRING bản địa, nơi nó thực sự nghĩa là như vậy.
Ded repeatator

1
@Ded repeatator, bạn đang nói về Liên kết và nhúng đối tượng của Microsoft phải không? Nếu bạn đang nhớ rằng OLE là từ những năm 90! Nhìn hiện tại, nhiều ngôn ngữ hiện đại (C #, Java, Swift) đang cung cấp các phương tiện để loại bỏ / xóa bỏ việc sử dụng null.
Erik Eidt

2
@ErikEidt Họ đang làm điều đó, một phần, bởi vì nullkhông có nghĩa là "thiếu thông tin". nullkhông có một cách giải thích có ý nghĩa duy nhất. Thay vào đó, vấn đề là bạn đối xử với nó như thế nào. nullđôi khi được sử dụng cho "thông tin bị thiếu", nhưng cũng cho "không có yếu tố" và cũng cho "lỗi xảy ra" hoặc "chưa được khởi tạo" hoặc "thông tin mâu thuẫn" hoặc một số điều tùy tiện, đột xuất.
Derek Elkins

-1. Đây là một quyết định được đưa ra bởi nhà phát triển, không phải bởi các lý thuyết trừu tượng. nullhoàn toàn thường xuyên được sử dụng có nghĩa là "không có dữ liệu." Đôi khi nó có ý nghĩa. Những lần khác, không.
jpmc26

13

Ngoài cú phápđiều kiện null , còn có một kỹ thuật khác để giảm bớt vấn đề này: đừng để biến của bạn tồn tại null.

Hãy xem xét một chức năng chấp nhận một bộ sưu tập như là một tham số. Nếu cho các mục đích của hàm nullvà trống là tương đương, bạn có thể đảm bảo rằng nó không bao giờ chứa nullở đầu:

public MyResult DoSomething(int a, IEnumerable<string> words)
{
    words = words ?? Enumerable.Empty<string>();

    if (!words.Any())
    {
        ...

Bạn có thể làm tương tự khi bạn tìm nạp các bộ sưu tập từ một số phương pháp khác:

var words = GetWords() ?? Enumerable.Empty<string>();

(Lưu ý rằng trong trường hợp bạn có quyền kiểm soát một chức năng như GetWordsnulltương đương với bộ sưu tập trống, tốt nhất là chỉ trả lại bộ sưu tập trống ở vị trí đầu tiên.)

Bây giờ bạn có thể thực hiện bất kỳ hoạt động nào bạn muốn trên bộ sưu tập. Điều này đặc biệt hữu ích nếu bạn cần thực hiện nhiều thao tác sẽ thất bại khi bộ sưu tập nullvà trong trường hợp bạn nhận được kết quả tương tự bằng cách lặp lại hoặc truy vấn vô số, nó sẽ cho phép loại bỏ ifhoàn toàn các điều kiện.


12

Tôi có sai không, với tư cách là khách hàng của mã này, hy vọng rằng ví dụ ((int []) null) .Any () sẽ trả về false?

Có, đơn giản vì bạn đang ở C # và hành vi đó được xác định rõ ràng và được ghi lại.

Nếu bạn đang tạo thư viện của riêng mình hoặc nếu bạn đang sử dụng một ngôn ngữ khác với văn hóa ngoại lệ khác thì sẽ hợp lý hơn nếu bạn mong đợi sai.

Cá nhân tôi cảm thấy như thể trả về false là một cách tiếp cận an toàn hơn làm cho chương trình của bạn mạnh mẽ hơn, nhưng ít nhất nó vẫn còn gây tranh cãi.


15
Điều đó 'mạnh mẽ' thường chỉ là ảo ảnh - nếu mã tạo ra mảng đột nhiên bắt đầu tạo NULL bất ngờ do lỗi hoặc sự cố khác, mã 'mạnh mẽ' của bạn sẽ che giấu vấn đề. Đó là hành vi thích hợp đôi khi, nhưng không phải là một mặc định an toàn.
DêInTheMachine

1
@GoatInTheMachine - Tôi không đồng ý về việc nó là một mặc định an toàn, nhưng bạn nói đúng rằng nó che giấu vấn đề và đôi khi hành vi đó là không mong muốn. Theo kinh nghiệm của tôi, việc che giấu vấn đề (hoặc tin tưởng các bài kiểm tra đơn vị để nắm bắt các vấn đề của mã khác) tốt hơn là làm hỏng ứng dụng của bạn bằng cách đưa ra các ngoại lệ không mong muốn. Ý tôi là thực sự - nếu mã cuộc gọi bị hỏng đủ để gửi thành null, thì cơ hội nào họ xử lý ngoại lệ đúng cách?
Telastyn

10
Nếu một cái gì đó bị hỏng, cho dù đó là mã của tôi, của một đồng nghiệp hay khách hàng khác, tôi muốn mã đó bị thất bại ngay lập tức và mọi người biết về nó hơn là nó được thực hiện trong một trạng thái không nhất quán trong một khoảng thời gian không xác định. Có thể làm điều này tất nhiên đôi khi là một sự xa xỉ và không phải lúc nào cũng phù hợp nhưng đó là cách tiếp cận tôi thích.
DêInTheMachine

1
@GoatInTheMachine - Tôi đồng ý rất nhiều thứ, nhưng các bộ sưu tập có xu hướng khác nhau. Đối xử với null như một bộ sưu tập trống không phải là một hành vi không nhất quán hoặc đáng ngạc nhiên.
Telastyn

8
Hãy tưởng tượng bạn có một mảng được điền từ một tài liệu JSON nhận được thông qua một yêu cầu web và trong sản xuất, một trong những khách hàng API của bạn đột nhiên gặp sự cố khi họ gửi qua JSON một phần không hợp lệ khiến bạn phải giải tuần tự hóa một mảng như NULL, thay vào đó: mã, tệ nhất, đã ném một ngoại lệ tham chiếu NULL ngay khi yêu cầu xấu đầu tiên xuất hiện, mà bạn thấy khi đăng nhập và ngay lập tức báo cho khách hàng sửa các yêu cầu bị hỏng của họ, HOẶC mã của bạn nói "ồ, mảng trống, không có gì làm!" và âm thầm viết 'giỏ mua hàng không có mặt hàng' cho db trong vài tuần?
DêInTheMachine

10

Nếu kiểm tra null lặp đi lặp lại làm phiền bạn, bạn có thể tạo phương thức mở rộng 'IsNullOrEmpty ()' của riêng mình, để phản chiếu phương thức String theo tên đó và bọc cả lệnh gọi null-check và .Any () vào một cuộc gọi.

Mặt khác, giải pháp, được đề cập bởi @ 17 trên 26 trong một bình luận dưới câu hỏi của bạn, cũng ngắn hơn phương pháp 'tiêu chuẩn', và rõ ràng hợp lý cho bất kỳ ai quen thuộc với cú pháp có điều kiện null mới.

if(myCollection?.Any() == true)

4
Bạn cũng có thể sử dụng ?? falsethay vì == true. Điều này có vẻ rõ ràng hơn (sạch hơn) đối với tôi vì nó chỉ xử lý trường hợp nullvà nó không thực sự dài dòng nữa. Cộng ==với việc kiểm tra Booleans thường kích hoạt phản xạ bịt miệng của tôi. =)
jpmc26

2
@ jpmc26: Tôi không biết nhiều về C #. Tại sao không myCollection?.Any()đủ?
Eric Duminil

6
@EricDuminil Do cách mà toán tử có điều kiện null hoạt động, myCollection?.Any()trả về hiệu quả Boolean null thay vì Boolean thông thường (nghĩa là bool? Thay vì bool). Không có chuyển đổi ngầm từ bool? đối với bool, và đó là một bool chúng ta cần trong ví dụ cụ thể này (để kiểm tra như một phần của ifđiều kiện). Do đó, chúng ta phải so sánh rõ ràng với truehoặc sử dụng toán tử hợp nhất null (??).
Steven Rands

@ jpmc26 ?? false khó đọc hơn myCollection? .Any () == true. Bất kể phản xạ bịt miệng của bạn là gì, == true là những gì bạn đang cố gắng kiểm tra ngầm. ?? false chỉ thêm tiếng ồn và bạn vẫn cần thực hiện đánh giá trong đầu về những gì xảy ra nếu null, null == true sẽ thất bại, hầu như không có người đọc thậm chí phải nhận thức được ?..
Ryan The Leach

2
@RyanTheLeach Tôi phải suy nghĩ nhiều hơn về việc liệu ==thực sự sẽ làm việc. Tôi đã biết những gì xảy ra với một Boolean bằng trực giác khi nó chỉ có thể truehoặc false; Tôi thậm chí không phải nghĩ về nó. Nhưng ném nullvào hỗn hợp, và bây giờ tôi phải tìm ra liệu điều đó ==có đúng không. ??thay vào đó chỉ cần loại bỏ nulltrường hợp, giảm nó trở lại truefalse, mà bạn đã hiểu ngay lập tức. Đối với phản xạ bịt miệng của tôi, khi tôi lần đầu tiên nhìn thấy nó, bản năng ngay lập tức của tôi là chỉ xóa nó vì nó thường vô dụng, điều mà bạn không muốn xảy ra.
jpmc26

8

Tôi có sai không, với tư cách là khách hàng của mã này, hy vọng rằng ví dụ ((int []) null) .Any () sẽ trả về false?

Nếu bạn tự hỏi về những kỳ vọng bạn phải suy nghĩ về ý định.

null có nghĩa là một cái gì đó rất khác với Enumerable.Empty<T>

Như đã đề cập trong câu trả lời của Erik Eidt , có một sự khác biệt về ý nghĩa giữa nullvà một bộ sưu tập trống.

Trước tiên chúng ta hãy nhìn vào cách chúng được sử dụng.

Cuốn sách Hướng dẫn thiết kế khung: Các quy ước, thành ngữ và mô hình cho các thư viện .NET có thể tái sử dụng, Ấn bản thứ 2 được viết bởi kiến ​​trúc sư Microsoft Krzysztof CwalinaBrad Abrams nêu cách thực hành tốt nhất sau đây:

X KHÔNG trả về giá trị null từ thuộc tính bộ sưu tập hoặc từ phương thức trả về bộ sưu tập. Trả về một bộ sưu tập trống hoặc một mảng trống thay thế.

Xem xét việc bạn gọi một phương thức cuối cùng là lấy dữ liệu từ cơ sở dữ liệu: Nếu bạn nhận được một mảng trống hoặc Enumerable.Empty<T>điều này đơn giản có nghĩa là không gian mẫu của bạn trống, tức là kết quả của bạn là một tập hợp trống . Nhận nulltrong bối cảnh này, tuy nhiên, sẽ biểu thị một trạng thái lỗi .

Trong cùng một lối suy nghĩ giống như khoai tây của Dan Wilson , thật hợp lý khi đặt câu hỏi về dữ liệu của bạn ngay cả khi đó là một tập hợp trống . Nhưng nó làm cho rất ít ý nghĩa, nếu không có bộ .


1
Cho dù chúng có ý nghĩa khác nhau là phụ thuộc nhiều vào bối cảnh. Thông thường, với mục đích của bất kỳ logic nào trong tầm tay, chúng đại diện cho các khái niệm tương đương. Không phải luôn luôn , nhưng thường xuyên.
jpmc26

1
@ jpmc26: Tôi nghĩ rằng điểm của câu trả lời này là để nói rằng bằng cách hướng dẫn mã hóa cho c #, chúng có một ý nghĩa khác. Bạn luôn có thể đúng mã trong đó null và rỗng giống nhau nếu bạn muốn. Và trong một số trường hợp, bạn cũng có thể làm điều tương tự cho dù nó là rỗng hay rỗng nhưng điều đó không có nghĩa là chúng có nghĩa tương tự.
Chris

@Chris Và tôi đang nói rằng các hướng dẫn mã hóa từ những người thiết kế ngôn ngữ không thể được sử dụng để thay thế cho việc đánh giá mã của bạn trong bối cảnh yêu cầu của bạn, các thư viện bạn đang sử dụng và các nhà phát triển khác mà bạn làm việc cùng. Ý tưởng rằng chúng sẽ không bao giờ tương đương trong bối cảnh là chủ nghĩa duy tâm trên trời. Chúng thậm chí có thể không tương đương với dữ liệu trong tay nói chung nhưng tương đương với việc tạo ra kết quả trong một số chức năng cụ thể mà bạn đang viết; trong trường hợp đó, bạn cần hỗ trợ cả hai nhưng đảm bảo chúng tạo ra kết quả như nhau.
jpmc26

Tôi có thể bối rối bởi những gì bạn đang nói nhưng khi null và rỗng tương đương với việc tạo ra kết quả Tôi không hiểu tại sao bạn không sử dụng riêng biệt là null và kiểm tra trống thay vì muốn một phương thức thực hiện cùng một lúc nhưng không cho phép bạn phân biệt chúng trong các tình huống khác ...
Chris

@Chris Tôi đang đề cập đến việc có các bối cảnh khác nhau (thường tương ứng với phạm vi). Hãy xem xét một chức năng chẳng hạn. nullvà trống có thể dẫn đến cùng một giá trị trả về cho hàm, nhưng điều đó không có nghĩa là nulltrống và có nghĩa chính xác điều tương tự trong phạm vi gọi.
jpmc26

3

Có nhiều câu trả lời giải thích tại sao nullvà trống rỗng là khác nhau và đủ ý kiến ​​cố gắng giải thích cả lý do tại sao chúng nên được đối xử khác nhau hay không. Tuy nhiên, bạn đang hỏi:

Tôi có sai không, với tư cách là khách hàng của mã này, hy vọng rằng ví dụ ((int []) null) .Any () sẽ trả về false?

Đó là một kỳ vọng hoàn toàn hợp lý . Bạn đúng như người khác ủng hộ cho hành vi hiện tại. Tôi đồng ý với triết lý thực hiện hiện tại nhưng các yếu tố lái xe không - chỉ - dựa trên những cân nhắc bên ngoài bối cảnh.

Cho rằng Any()không có vị ngữ về cơ bản Count() > 0thì bạn mong đợi gì từ đoạn trích này?

List<int> list = null;
if (list.Count > 0) {}

Hoặc chung chung:

List<int> list = null;
if (list.Foo()) {}

Tôi cho rằng bạn mong đợi NullReferenceException.

  • Any()là một phương thức mở rộng, nó nên tích hợp trơn tru với đối tượng mở rộng, sau đó ném một ngoại lệ là điều ít gây ngạc nhiên nhất.
  • Không phải mọi ngôn ngữ .NET đều hỗ trợ các phương thức mở rộng.
  • Bạn luôn có thể gọi Enumerable.Any(null)ở đó bạn chắc chắn mong đợi ArgumentNullException. Đó là cùng một phương pháp và nó phải phù hợp với - có thể - gần như MỌI THỨ khác trong Khung.
  • Truy cập một nullđối tượng là một lỗi lập trình , khung không nên thực thi null dưới dạng giá trị ma thuật . Nếu bạn sử dụng nó theo cách đó thì bạn có trách nhiệm đối phó với nó.
  • khăng khăng , bạn nghĩ một cách và tôi nghĩ một cách khác. Khung nên càng không được khuyến khích càng tốt.
  • Nếu bạn có một trường hợp đặc biệt thì bạn phải nhất quán : bạn phải đưa ra quyết định ngày càng cao hơn về tất cả các phương pháp mở rộng khác: nếu Count()có vẻ là một quyết định dễ dàng thì Where()không. Thế còn Max()? Nó ném một ngoại lệ cho danh sách EMPTY, không nên ném nó cho một danh sách null?

Những gì các nhà thiết kế thư viện đã làm trước LINQ là giới thiệu các phương thức rõ ràng khi nào nulllà một giá trị hợp lệ (ví dụ String.IsNullOrEmpty()) thì họ HAD phải phù hợp với triết lý thiết kế hiện có . Điều đó nói rằng, ngay cả khi khá đơn giản để viết, hai phương pháp EmptyIfNull()EmptyOrNull()có thể có ích.


1

Jim được cho là để khoai tây trong mỗi túi. Nếu không tôi sẽ giết anh ta.

Jim có một túi với năm củ khoai tây trong đó. Có .Any()khoai tây trong túi?

"Vâng," bạn nói. <= đúng

Ok để Jim sống lần này.

Jim lấy tất cả khoai tây ra và ăn chúng. Có .Any () khoai tây trong túi?

"Không," bạn nói. <= sai

Thời gian để giết Jim.

Jim thiêu rụi hoàn toàn chiếc túi trong lửa. Có khoai tây nào trong túi không?

"Không có túi." <= ArgumentNullException

Jim nên sống hay chết? Vâng, chúng tôi đã không mong đợi điều này vì vậy tôi cần một phán quyết. Là để Jim thoát khỏi điều này một lỗi hay không?

Bạn có thể sử dụng các chú thích để báo hiệu rằng bạn không đưa ra bất kỳ shenanigans null nào theo cách này.

public bool Any( [NotNull] List bag ) 

Nhưng chuỗi công cụ của bạn phải hỗ trợ nó. Điều đó có nghĩa là bạn có khả năng vẫn sẽ kết thúc việc viết séc.


0

Nếu điều này làm phiền bạn rất nhiều, tôi đề nghị một phương pháp mở rộng đơn giản.

static public IEnumerable<T> NullToEmpty<T>(this IEnumerable<T> source)
{
    return (source == null) ? Enumerable.Empty<T>() : source;
}

Bây giờ bạn có thể làm điều này:

List<string> list = null;
var flag = list.NullToEmpty().Any( s => s == "Foo" );

... Và cờ sẽ được đặt thành false.


2
Tự nhiên hơn để viết returntuyên bố như:return source ?? Enumerable.Empty<T>();
Jeppe Stig Nielsen

Điều này không trả lời câu hỏi.
Kenneth K.

@ KennethK.nhưng nó dường như giải quyết vấn đề được trình bày.
RQDQ

0

Đây là câu hỏi về phương pháp mở rộng của C # và triết lý thiết kế của họ, vì vậy tôi nghĩ rằng cách tốt nhất để trả lời câu hỏi này là trích dẫn tài liệu của MSDN về mục đích của phương pháp mở rộng :

Các phương thức mở rộng cho phép bạn "thêm" các phương thức vào các loại hiện có mà không cần tạo một loại dẫn xuất mới, biên dịch lại hoặc sửa đổi loại ban đầu. Các phương thức mở rộng là một loại phương thức tĩnh đặc biệt, nhưng chúng được gọi như thể chúng là các phương thức thể hiện trên kiểu mở rộng. Đối với mã máy khách được viết bằng C #, F # và Visual Basic, không có sự khác biệt rõ ràng giữa việc gọi một phương thức mở rộng và các phương thức thực sự được xác định trong một loại.

Giáo dục

Nói chung, chúng tôi khuyên bạn nên thực hiện các phương pháp mở rộng một cách tiết kiệm và chỉ khi bạn phải thực hiện. Bất cứ khi nào có thể, mã máy khách phải mở rộng một loại hiện có nên làm như vậy bằng cách tạo một loại mới xuất phát từ loại hiện có. Để biết thêm thông tin, xem Kế thừa.

Khi sử dụng phương thức tiện ích mở rộng để mở rộng loại có mã nguồn bạn không thể thay đổi, bạn sẽ gặp rủi ro khi thay đổi trong việc triển khai loại sẽ khiến phương thức tiện ích mở rộng của bạn bị hỏng.

Nếu bạn thực hiện các phương thức mở rộng cho một loại nhất định, hãy nhớ các điểm sau:

  • Một phương thức mở rộng sẽ không bao giờ được gọi nếu nó có cùng chữ ký với một phương thức được xác định trong loại.
  • Các phương thức mở rộng được đưa vào phạm vi ở cấp độ không gian tên. Ví dụ, nếu bạn có nhiều lớp tĩnh chứa các phương thức mở rộng trong một không gian tên duy nhất được đặt tên Extensions, tất cả chúng sẽ được đưa vào phạm vi bởi lệnh using Extensions;.

Tóm lại, các phương thức mở rộng được thiết kế để thêm các phương thức cá thể vào một loại cụ thể, ngay cả khi các nhà phát triển không thể làm như vậy trực tiếp. Và bởi vì các phương thức cá thể sẽ luôn ghi đè các phương thức mở rộng nếu có (nếu được gọi bằng cú pháp phương thức cá thể), điều này chỉ nên được thực hiện nếu bạn không thể trực tiếp thêm một phương thức hoặc mở rộng lớp. *

Nói cách khác, một phương thức mở rộng sẽ hoạt động giống như một phương thức cá thể, bởi vì cuối cùng nó có thể trở thành một phương thức cá thể. Và bởi vì một phương thức cá thể sẽ ném nếu đối tượng mà nó được gọi là null, nên phương thức mở rộng cũng vậy.


* Như một lưu ý phụ, đây chính xác là tình huống mà các nhà thiết kế của LINQ phải đối mặt: khi C # 3.0 được phát hành, đã có hàng triệu khách hàng đang sử dụng System.Collections.IEnumerableSystem.Collections.Generic.IEnumerable<T>cả trong bộ sưu tập của họ và trong foreachcác vòng lặp. Những lớp học trở lại IEnumeratorđối tượng mà chỉ có hai phương pháp CurrentMoveNext, vì vậy thêm bất kỳ phương pháp dụ yêu cầu bổ sung, chẳng hạn như Count, Anyvv, sẽ được phá vỡ những hàng triệu khách hàng. Vì vậy, để cung cấp chức năng này (đặc biệt là vì nó có thể được triển khai theo cách dễ dàng CurrentMoveNexttương đối), họ đã phát hành nó dưới dạng phương thức mở rộng, có thể được áp dụng cho bất kỳ hiện tại nàoIEnumerablecá thể và cũng có thể được các lớp thực hiện theo những cách hiệu quả hơn. Nếu các nhà thiết kế của C # quyết định phát hành LINQ vào ngày đầu tiên, nó sẽ được cung cấp dưới dạng phương thức ví dụ IEnumerablevà có lẽ họ đã thiết kế một số loại hệ thống để cung cấp các triển khai giao diện mặc định của các phương thức đó.


0

Tôi nghĩ rằng điểm nổi bật ở đây là bằng cách trả về sai thay vì ném một ngoại lệ, bạn đang che giấu thông tin có thể liên quan đến các trình đọc / sửa đổi mã trong tương lai của mã của bạn.

Nếu danh sách có thể là null, tôi khuyên bạn nên có một đường dẫn logic riêng cho điều đó, vì trong tương lai, ai đó có thể thêm một số logic dựa trên danh sách (như thêm) vào {if khác của bạn, dẫn đến một ngoại lệ không mong muốn mà họ không có lý do để dự đoán.

Khả năng dễ đọc và khả năng bảo trì "Tôi phải viết thêm một điều kiện" mỗi lần.


0

Theo nguyên tắc chung, tôi viết hầu hết mã của mình để cho rằng người gọi có trách nhiệm không cung cấp cho tôi dữ liệu null, chủ yếu là vì các lý do được nêu trong Nói Không với Null (và các bài đăng tương tự khác). Khái niệm null thường không được hiểu rõ và vì lý do đó, nên khởi tạo các biến của bạn trước thời hạn, càng nhiều càng thiết thực. Giả sử bạn đang làm việc với một API hợp lý, lý tưởng nhất là bạn không bao giờ nên lấy lại giá trị null, vì vậy bạn không bao giờ phải kiểm tra null. Quan điểm của null, như các câu trả lời khác đã nêu, là đảm bảo rằng bạn có một đối tượng hợp lệ (ví dụ: "túi") để làm việc trước khi tiếp tục. Đây không phải là một tính năng của Any, mà thay vào đó là một tính năng của ngôn ngữChính nó. Bạn không thể thực hiện hầu hết các hoạt động trên một đối tượng null, vì nó không tồn tại. Giống như nếu tôi yêu cầu bạn lái xe đến cửa hàng trong xe của tôi, ngoại trừ tôi không sở hữu xe hơi, vì vậy bạn không có cách nào để sử dụng xe của tôi để lái xe đến cửa hàng. Thật vô lý khi thực hiện một thao tác trên một cái gì đó thực sự không tồn tại.

Có những trường hợp thực tế khi sử dụng null, chẳng hạn như khi bạn thực sự không có sẵn thông tin được yêu cầu (ví dụ: nếu tôi hỏi bạn bao nhiêu tuổi, thì lựa chọn khả dĩ nhất để bạn trả lời vào thời điểm này là "Tôi không biết ", Đó là giá trị null). Tuy nhiên, trong hầu hết các trường hợp, ít nhất bạn nên biết liệu các biến của mình có được khởi tạo hay không. Và nếu bạn không, thì tôi khuyên bạn nên thắt chặt mã của mình. Điều đầu tiên tôi làm trong bất kỳ chương trình hoặc chức năng nào là khởi tạo một giá trị trước khi tôi sử dụng nó. Rất hiếm khi tôi cần kiểm tra các giá trị null, bởi vì tôi có thể đảm bảo rằng các biến của tôi không phải là null. Đó là một thói quen tốt để tham gia và bạn càng thường xuyên nhớ khởi tạo các biến của mình, bạn càng cần thường xuyên kiểm tra null.

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.