Đó là một câu hỏi chung (nhưng tôi đang sử dụng C #), cách tốt nhất (thực tiễn tốt nhất) là gì, bạn có trả về bộ sưu tập rỗng hoặc rỗng cho phương thức có bộ sưu tập làm kiểu trả về không?
Đó là một câu hỏi chung (nhưng tôi đang sử dụng C #), cách tốt nhất (thực tiễn tốt nhất) là gì, bạn có trả về bộ sưu tập rỗng hoặc rỗng cho phương thức có bộ sưu tập làm kiểu trả về không?
Câu trả lời:
Bộ sưu tập trống. Luôn luôn.
Điều này thật tệ:
if(myInstance.CollectionProperty != null)
{
foreach(var item in myInstance.CollectionProperty)
/* arrgh */
}
Nó được coi là một cách thực hành tốt nhất để KHÔNG BAO GIỜ quay trở lại null
khi trả lại một bộ sưu tập hoặc vô số. LUÔN LUÔN trả lại một vô số / bộ sưu tập trống. Nó ngăn chặn những điều vô nghĩa đã nói ở trên, và ngăn chặn chiếc xe của bạn bị đồng nghiệp và những người sử dụng các lớp học của bạn làm phiền.
Khi nói về tài sản, luôn đặt tài sản của bạn một lần và quên nó
public List<Foo> Foos {public get; private set;}
public Bar() { Foos = new List<Foo>(); }
Trong .NET 4.6.1, bạn có thể ngưng tụ khá nhiều thứ này:
public List<Foo> Foos { get; } = new List<Foo>();
Khi nói về các phương thức trả về liệt kê, bạn có thể dễ dàng trả về một vô số trống thay vì null
...
public IEnumerable<Foo> GetMyFoos()
{
return InnerGetFoos() ?? Enumerable.Empty<Foo>();
}
Sử dụng Enumerable.Empty<T>()
có thể được coi là hiệu quả hơn so với trả về, ví dụ, một bộ sưu tập hoặc mảng trống mới.
IEnumerable
hay ICollection
không quan trọng. Dù sao, nếu bạn chọn thứ gì đó ICollection
họ cũng trả lại null
... Tôi muốn họ trả lại một bộ sưu tập trống, nhưng tôi tình cờ thấy họ trở về null
, vì vậy tôi nghĩ tôi chỉ đề cập đến nó ở đây. Tôi muốn nói mặc định của một bộ sưu tập là vô số không rỗng. Tôi không biết đó là một chủ đề nhạy cảm.
Từ Hướng dẫn thiết kế khung Phiên bản 2 (trang 256):
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ế.
Đây là một bài viết thú vị khác về lợi ích của việc không trả về null (Tôi đã cố gắng tìm một cái gì đó trên blog của Brad Abram và anh ấy đã liên kết với bài viết).
Chỉnh sửa- như Eric Lippert hiện đã nhận xét cho câu hỏi ban đầu, tôi cũng muốn liên kết đến bài viết xuất sắc của mình .
Phụ thuộc vào hợp đồng của bạn và trường hợp cụ thể của bạn . Nói chung , tốt nhất là trả lại các bộ sưu tập trống , nhưng đôi khi ( hiếm khi ):
null
có thể có nghĩa là một cái gì đó cụ thể hơn;null
.Một số ví dụ cụ thể:
null
có nghĩa là phần tử bị thiếu, trong khi một bộ sưu tập trống sẽ hiển thị dự phòng (và có thể không chính xác)<collection />
Có một điểm khác chưa được đề cập. Hãy xem xét các mã sau đây:
public static IEnumerable<string> GetFavoriteEmoSongs()
{
yield break;
}
Ngôn ngữ C # sẽ trả về một điều tra viên trống khi gọi phương thức này. Do đó, để phù hợp với thiết kế ngôn ngữ (và, do đó, kỳ vọng của lập trình viên) nên trả lại một bộ sưu tập trống.
Trống rỗng thân thiện với người tiêu dùng hơn nhiều.
Có một phương pháp rõ ràng để tạo thành một vô số trống:
Enumerable.Empty<Element>()
Dường như với tôi rằng bạn nên trả về giá trị đúng về mặt ngữ nghĩa trong bối cảnh, bất cứ điều gì có thể. Một quy tắc nói rằng "luôn trả lại một bộ sưu tập trống" có vẻ hơi đơn giản đối với tôi.
Giả sử trong một hệ thống của một bệnh viện, chúng ta có một chức năng được cho là trả về một danh sách tất cả các lần nhập viện trước đó trong 5 năm qua. Nếu khách hàng chưa đến bệnh viện, việc trả lại một danh sách trống sẽ rất hợp lý. Nhưng điều gì sẽ xảy ra nếu khách hàng bỏ trống phần biểu mẫu nhận tiền đó? Chúng tôi cần một giá trị khác để phân biệt "danh sách trống" với "không trả lời" hoặc "không biết". Chúng ta có thể đưa ra một ngoại lệ, nhưng nó không nhất thiết là một điều kiện lỗi và nó không nhất thiết đẩy chúng ta ra khỏi dòng chương trình bình thường.
Tôi thường bị thất vọng bởi các hệ thống không thể phân biệt giữa không và không có câu trả lời. Tôi đã có một số lần hệ thống yêu cầu tôi nhập một số số, tôi nhập số không và tôi nhận được thông báo lỗi cho tôi biết rằng tôi phải nhập một giá trị trong trường này. Tôi chỉ làm: Tôi đã nhập số không! Nhưng nó sẽ không chấp nhận số 0 vì nó không thể phân biệt nó với không có câu trả lời.
Trả lời Saunders:
Có, tôi cho rằng có sự khác biệt giữa "Người không trả lời câu hỏi" và "Câu trả lời là số không". Đó là điểm của đoạn cuối câu trả lời của tôi. Nhiều chương trình không thể phân biệt "không biết" với khoảng trống hoặc số không, có vẻ như là một lỗ hổng nghiêm trọng. Ví dụ, tôi đã mua sắm một ngôi nhà cách đây một năm. Tôi đã đi đến một trang web bất động sản và có nhiều ngôi nhà được liệt kê với giá yêu cầu là 0 đô la. Nghe có vẻ khá tốt với tôi: Họ đang tặng những ngôi nhà này miễn phí! Nhưng tôi chắc chắn rằng thực tế đáng buồn là họ đã không nhập giá. Trong trường hợp đó, bạn có thể nói, "Chà, NGHIÊM TÚC bằng không có nghĩa là họ không nhập giá - không ai sẽ cho một ngôi nhà miễn phí." Nhưng trang web cũng liệt kê giá chào bán trung bình của các ngôi nhà ở các thị trấn khác nhau. Tôi không thể không tự hỏi nếu trung bình không bao gồm các số không, do đó đưa ra mức trung bình thấp không chính xác cho một số nơi. tức là trung bình 100.000 đô la là bao nhiêu; $ 120.000; và "không biết"? Về mặt kỹ thuật, câu trả lời là "không biết". Những gì chúng ta có thể thực sự muốn thấy là $ 110.000. Nhưng những gì chúng ta có thể nhận được là 73.333 đô la, điều này hoàn toàn sai. Ngoài ra, điều gì xảy ra nếu chúng tôi gặp sự cố này trên một trang web nơi người dùng có thể đặt hàng trực tuyến? (Không có khả năng cho bất động sản, nhưng tôi chắc chắn bạn đã thấy nó được thực hiện cho nhiều sản phẩm khác.) Chúng tôi có thực sự muốn "giá chưa được chỉ định" được hiểu là "miễn phí" không? do đó đưa ra mức trung bình thấp không chính xác cho một số nơi. tức là trung bình 100.000 đô la là bao nhiêu; $ 120.000; và "không biết"? Về mặt kỹ thuật, câu trả lời là "không biết". Những gì chúng ta có thể thực sự muốn thấy là $ 110.000. Nhưng những gì chúng ta có thể nhận được là 73.333 đô la, điều này hoàn toàn sai. Ngoài ra, điều gì xảy ra nếu chúng tôi gặp sự cố này trên một trang web nơi người dùng có thể đặt hàng trực tuyến? (Không có khả năng cho bất động sản, nhưng tôi chắc chắn bạn đã thấy nó được thực hiện cho nhiều sản phẩm khác.) Chúng tôi có thực sự muốn "giá chưa được chỉ định" được hiểu là "miễn phí" không? do đó đưa ra mức trung bình thấp không chính xác cho một số nơi. tức là trung bình 100.000 đô la là bao nhiêu; $ 120.000; và "không biết"? Về mặt kỹ thuật, câu trả lời là "không biết". Những gì chúng ta có thể thực sự muốn thấy là $ 110.000. Nhưng những gì chúng ta có thể nhận được là 73.333 đô la, điều này hoàn toàn sai. Ngoài ra, điều gì xảy ra nếu chúng tôi gặp sự cố này trên một trang web nơi người dùng có thể đặt hàng trực tuyến? (Không có khả năng cho bất động sản, nhưng tôi chắc chắn bạn đã thấy nó được thực hiện cho nhiều sản phẩm khác.) Chúng tôi có thực sự muốn "giá chưa được chỉ định" được hiểu là "miễn phí" không? Điều đó sẽ hoàn toàn sai. Ngoài ra, điều gì xảy ra nếu chúng tôi gặp sự cố này trên một trang web nơi người dùng có thể đặt hàng trực tuyến? (Không có khả năng cho bất động sản, nhưng tôi chắc chắn bạn đã thấy nó được thực hiện cho nhiều sản phẩm khác.) Chúng tôi có thực sự muốn "giá chưa được chỉ định" được hiểu là "miễn phí" không? Điều đó sẽ hoàn toàn sai. Ngoài ra, điều gì xảy ra nếu chúng tôi gặp sự cố này trên một trang web nơi người dùng có thể đặt hàng trực tuyến? (Không có khả năng cho bất động sản, nhưng tôi chắc chắn bạn đã thấy nó được thực hiện cho nhiều sản phẩm khác.) Chúng tôi có thực sự muốn "giá chưa được chỉ định" được hiểu là "miễn phí" không?
RE có hai chức năng riêng biệt, một "có bất kỳ?" và một "nếu vậy, nó là gì?" Vâng, bạn chắc chắn có thể làm điều đó, nhưng tại sao bạn muốn? Bây giờ chương trình gọi phải thực hiện hai cuộc gọi thay vì một cuộc gọi. Điều gì xảy ra nếu một lập trình viên không gọi được "bất kỳ?" và đi thẳng đến "nó là gì?" ? Chương trình sẽ trả về một số không dẫn đầu sai? Ném một ngoại lệ? Trả về một giá trị không xác định? Nó tạo ra nhiều mã hơn, nhiều công việc hơn và nhiều lỗi tiềm ẩn hơn.
Lợi ích duy nhất tôi thấy là nó cho phép bạn tuân thủ một quy tắc tùy ý. Có bất kỳ lợi thế cho quy tắc này làm cho nó có giá trị rắc rối của việc tuân theo nó? Nếu không, tại sao phải bận tâm?
Trả lời Jammycakes:
Xem xét những gì mã thực tế sẽ trông như thế nào. Tôi biết câu hỏi đã nói C # nhưng xin lỗi nếu tôi viết Java. C # của tôi không quá sắc nét và nguyên tắc là như vậy.
Với lợi nhuận không:
HospList list=patient.getHospitalizationList(patientId);
if (list==null)
{
// ... handle missing list ...
}
else
{
for (HospEntry entry : list)
// ... do whatever ...
}
Với chức năng riêng biệt:
if (patient.hasHospitalizationList(patientId))
{
// ... handle missing list ...
}
else
{
HospList=patient.getHospitalizationList(patientId))
for (HospEntry entry : list)
// ... do whatever ...
}
Đó thực sự là một dòng hoặc hai mã ít hơn với trả về null, vì vậy nó không gây gánh nặng nhiều hơn cho người gọi, nó ít hơn.
Tôi không thấy cách nó tạo ra vấn đề KHÔ. Nó không giống như chúng ta phải thực hiện cuộc gọi hai lần. Nếu chúng ta luôn muốn làm điều tương tự khi danh sách không tồn tại, có lẽ chúng ta có thể đẩy việc xử lý xuống chức năng get-list thay vì yêu cầu người gọi làm điều đó, và vì vậy việc đưa mã vào người gọi sẽ là vi phạm DRY. Nhưng chúng tôi gần như chắc chắn không muốn luôn luôn làm điều tương tự. Trong các chức năng mà chúng ta phải có danh sách để xử lý, danh sách bị thiếu là một lỗi có thể dừng xử lý. Nhưng trên màn hình chỉnh sửa, chúng tôi chắc chắn không muốn dừng xử lý nếu họ chưa nhập dữ liệu: chúng tôi muốn cho phép họ nhập dữ liệu. Vì vậy, việc xử lý "không có danh sách" phải được thực hiện ở cấp độ người gọi bằng cách này hay cách khác. Và cho dù chúng ta làm điều đó với trả về null hoặc một hàm riêng biệt sẽ không có sự khác biệt so với nguyên tắc lớn hơn.
Chắc chắn, nếu người gọi không kiểm tra null, chương trình có thể thất bại với ngoại lệ con trỏ null. Nhưng nếu có một chức năng "có bất kỳ" riêng biệt nào và người gọi không gọi chức năng đó mà gọi một cách mù quáng là chức năng "nhận danh sách" thì chuyện gì sẽ xảy ra? Nếu nó ném một ngoại lệ hoặc thất bại, thì, điều đó khá giống với những gì sẽ xảy ra nếu nó trả về null và không kiểm tra nó. Nếu nó trả về một danh sách trống, điều đó chỉ sai. Bạn không thể phân biệt giữa "Tôi có một danh sách không có phần tử nào" và "Tôi không có danh sách". Nó giống như trả về 0 cho giá khi người dùng không nhập bất kỳ giá nào: nó chỉ sai.
Tôi không thấy cách gắn một thuộc tính bổ sung vào bộ sưu tập giúp. Người gọi vẫn phải kiểm tra nó. Làm thế nào là tốt hơn so với việc kiểm tra null? Một lần nữa, điều tồi tệ nhất tuyệt đối có thể xảy ra là lập trình viên quên kiểm tra nó và đưa ra kết quả không chính xác.
Hàm trả về null không phải là một điều ngạc nhiên nếu lập trình viên quen thuộc với khái niệm null có nghĩa là "không có giá trị", mà tôi nghĩ rằng bất kỳ lập trình viên có thẩm quyền nào cũng nên nghe, cho dù anh ta nghĩ đó có phải là ý tưởng tốt hay không. Tôi nghĩ rằng có một chức năng riêng biệt là một vấn đề "bất ngờ". Nếu một lập trình viên không quen thuộc với API, khi anh ta chạy thử nghiệm không có dữ liệu, anh ta sẽ nhanh chóng phát hiện ra rằng đôi khi anh ta nhận lại được một giá trị rỗng. Nhưng làm thế nào anh ta phát hiện ra sự tồn tại của một chức năng khác trừ khi anh ta có thể có một chức năng như vậy và anh ta kiểm tra tài liệu, và tài liệu này hoàn chỉnh và dễ hiểu? Tôi muốn có một chức năng luôn mang lại cho tôi một phản hồi có ý nghĩa, thay vì hai chức năng mà tôi phải biết và nhớ để gọi cả hai.
Nếu một bộ sưu tập trống có ý nghĩa về mặt ngữ nghĩa, đó là những gì tôi muốn trả lại. Trả lại một bộ sưu tập trống để GetMessagesInMyInbox()
liên lạc "bạn thực sự không có bất kỳ tin nhắn nào trong hộp thư đến của mình", trong khi trả lạinull
có thể hữu ích để liên lạc rằng không đủ dữ liệu để nói danh sách có thể được trả về như thế nào.
null
giá trị chắc chắn không có vẻ hợp lý, tôi đã suy nghĩ theo các thuật ngữ chung hơn về điều đó. Các trường hợp ngoại lệ cũng rất tuyệt để truyền đạt thực tế rằng có điều gì đó không ổn, nhưng nếu "dữ liệu không đủ" được đề cập là hoàn toàn được mong đợi, thì việc ném một ngoại lệ sẽ có thiết kế kém. Tôi đang nghĩ về một kịch bản trong đó hoàn toàn có thể xảy ra và không có lỗi nào cho phương thức đôi khi không thể tính toán được phản hồi.
Trả về null có thể hiệu quả hơn, vì không có đối tượng mới nào được tạo. Tuy nhiên, nó cũng thường yêu cầu null
kiểm tra (hoặc xử lý ngoại lệ.)
Về mặt ngữ nghĩa null
và một danh sách trống không có nghĩa tương tự. Sự khác biệt là tinh tế và một lựa chọn có thể tốt hơn so với lựa chọn khác trong các trường hợp cụ thể.
Bất kể sự lựa chọn của bạn, tài liệu để tránh nhầm lẫn.
Người ta có thể lập luận rằng lý do đằng sau Null Object Pattern tương tự như lý do ủng hộ việc trả lại bộ sưu tập trống.
Tùy theo hoàn cảnh. Nếu đó là một trường hợp đặc biệt, sau đó trả về null. Nếu hàm chỉ xảy ra để trả về một bộ sưu tập trống, thì rõ ràng trả về đó là ok. Tuy nhiên, trả lại một bộ sưu tập trống dưới dạng trường hợp đặc biệt vì các tham số không hợp lệ hoặc lý do khác KHÔNG phải là ý tưởng hay, vì nó che giấu một điều kiện trường hợp đặc biệt.
Trên thực tế, trong trường hợp này tôi thường thích ném một ngoại lệ để đảm bảo rằng nó THỰC SỰ không bị bỏ qua :)
Nói rằng nó làm cho mã mạnh mẽ hơn (bằng cách trả về một bộ sưu tập trống) vì chúng không phải xử lý điều kiện null là xấu, vì nó chỉ đơn giản là che giấu một vấn đề cần được xử lý bởi mã gọi.
Tôi sẽ lập luận rằng đó null
không giống như một bộ sưu tập trống và bạn nên chọn cái nào đại diện tốt nhất cho những gì bạn sẽ trở lại. Trong hầu hết các trường hợpnull
là không có gì (ngoại trừ trong SQL). Một bộ sưu tập trống là một cái gì đó, mặc dù một cái gì đó trống rỗng.
Nếu bạn phải chọn cái này hay cái khác, tôi sẽ nói rằng bạn nên hướng tới một bộ sưu tập trống thay vì null. Nhưng đôi khi một bộ sưu tập trống không giống với giá trị null.
Hãy suy nghĩ luôn có lợi cho khách hàng của bạn (đang sử dụng api của bạn):
Việc trả lại 'null' rất thường xuyên gây ra sự cố với các máy khách không xử lý kiểm tra null một cách chính xác, điều này gây ra một NullPulumException trong thời gian chạy. Tôi đã thấy các trường hợp kiểm tra null bị thiếu như vậy buộc phải có vấn đề sản xuất ưu tiên (một khách hàng đã sử dụng foreach (...) trên giá trị null). Trong quá trình thử nghiệm, sự cố không xảy ra, vì dữ liệu được vận hành hơi khác nhau.
Tôi muốn đưa ra giải thích ở đây, với ví dụ phù hợp.
Hãy xem xét một trường hợp ở đây ..
int totalValue = MySession.ListCustomerAccounts()
.FindAll(ac => ac.AccountHead.AccountHeadID
== accountHead.AccountHeadID)
.Sum(account => account.AccountValue);
Ở đây hãy xem xét các chức năng tôi đang sử dụng ..
1. ListCustomerAccounts() // User Defined
2. FindAll() // Pre-defined Library Function
Tôi có thể dễ dàng sử dụng ListCustomerAccount
và FindAll
thay vì.,
int totalValue = 0;
List<CustomerAccounts> custAccounts = ListCustomerAccounts();
if(custAccounts !=null ){
List<CustomerAccounts> custAccountsFiltered =
custAccounts.FindAll(ac => ac.AccountHead.AccountHeadID
== accountHead.AccountHeadID );
if(custAccountsFiltered != null)
totalValue = custAccountsFiltered.Sum(account =>
account.AccountValue).ToString();
}
LƯU Ý: Vì AccountValue không có null
, nên hàm Sum () sẽ không trả về null
. Do đó tôi có thể sử dụng trực tiếp.
Trả lại một bộ sưu tập trống là tốt hơn trong hầu hết các trường hợp.
Lý do cho điều đó là sự thuận tiện trong việc thực hiện của người gọi, hợp đồng nhất quán và thực hiện dễ dàng hơn.
Nếu một phương thức trả về null để chỉ ra kết quả trống, người gọi phải thực hiện bộ điều hợp kiểm tra null ngoài việc liệt kê. Mã này sau đó được sao chép trong nhiều người gọi khác nhau, vậy tại sao không đặt bộ điều hợp này vào bên trong phương thức để nó có thể được sử dụng lại.
Việc sử dụng null hợp lệ cho IEnumerable có thể là dấu hiệu của kết quả vắng mặt hoặc lỗi hoạt động, nhưng trong trường hợp này cần xem xét các kỹ thuật khác, chẳng hạn như ném ngoại lệ.
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
namespace StackOverflow.EmptyCollectionUsageTests.Tests
{
/// <summary>
/// Demonstrates different approaches for empty collection results.
/// </summary>
class Container
{
/// <summary>
/// Elements list.
/// Not initialized to an empty collection here for the purpose of demonstration of usage along with <see cref="Populate"/> method.
/// </summary>
private List<Element> elements;
/// <summary>
/// Gets elements if any
/// </summary>
/// <returns>Returns elements or empty collection.</returns>
public IEnumerable<Element> GetElements()
{
return elements ?? Enumerable.Empty<Element>();
}
/// <summary>
/// Initializes the container with some results, if any.
/// </summary>
public void Populate()
{
elements = new List<Element>();
}
/// <summary>
/// Gets elements. Throws <see cref="InvalidOperationException"/> if not populated.
/// </summary>
/// <returns>Returns <see cref="IEnumerable{T}"/> of <see cref="Element"/>.</returns>
public IEnumerable<Element> GetElementsStrict()
{
if (elements == null)
{
throw new InvalidOperationException("You must call Populate before calling this method.");
}
return elements;
}
/// <summary>
/// Gets elements, empty collection or nothing.
/// </summary>
/// <returns>Returns <see cref="IEnumerable{T}"/> of <see cref="Element"/>, with zero or more elements, or null in some cases.</returns>
public IEnumerable<Element> GetElementsInconvenientCareless()
{
return elements;
}
/// <summary>
/// Gets elements or nothing.
/// </summary>
/// <returns>Returns <see cref="IEnumerable{T}"/> of <see cref="Element"/>, with elements, or null in case of empty collection.</returns>
/// <remarks>We are lucky that elements is a List, otherwise enumeration would be needed.</remarks>
public IEnumerable<Element> GetElementsInconvenientCarefull()
{
if (elements == null || elements.Count == 0)
{
return null;
}
return elements;
}
}
class Element
{
}
/// <summary>
/// http://stackoverflow.com/questions/1969993/is-it-better-to-return-null-or-empty-collection/
/// </summary>
class EmptyCollectionTests
{
private Container container;
[SetUp]
public void SetUp()
{
container = new Container();
}
/// <summary>
/// Forgiving contract - caller does not have to implement null check in addition to enumeration.
/// </summary>
[Test]
public void UseGetElements()
{
Assert.AreEqual(0, container.GetElements().Count());
}
/// <summary>
/// Forget to <see cref="Container.Populate"/> and use strict method.
/// </summary>
[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void WrongUseOfStrictContract()
{
container.GetElementsStrict().Count();
}
/// <summary>
/// Call <see cref="Container.Populate"/> and use strict method.
/// </summary>
[Test]
public void CorrectUsaOfStrictContract()
{
container.Populate();
Assert.AreEqual(0, container.GetElementsStrict().Count());
}
/// <summary>
/// Inconvenient contract - needs a local variable.
/// </summary>
[Test]
public void CarefulUseOfCarelessMethod()
{
var elements = container.GetElementsInconvenientCareless();
Assert.AreEqual(0, elements == null ? 0 : elements.Count());
}
/// <summary>
/// Inconvenient contract - duplicate call in order to use in context of an single expression.
/// </summary>
[Test]
public void LameCarefulUseOfCarelessMethod()
{
Assert.AreEqual(0, container.GetElementsInconvenientCareless() == null ? 0 : container.GetElementsInconvenientCareless().Count());
}
[Test]
public void LuckyCarelessUseOfCarelessMethod()
{
// INIT
var praySomeoneCalledPopulateBefore = (Action)(()=>container.Populate());
praySomeoneCalledPopulateBefore();
// ACT //ASSERT
Assert.AreEqual(0, container.GetElementsInconvenientCareless().Count());
}
/// <summary>
/// Excercise <see cref="ArgumentNullException"/> because of null passed to <see cref="Enumerable.Count{TSource}(System.Collections.Generic.IEnumerable{TSource})"/>
/// </summary>
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void UnfortunateCarelessUseOfCarelessMethod()
{
Assert.AreEqual(0, container.GetElementsInconvenientCareless().Count());
}
/// <summary>
/// Demonstrates the client code flow relying on returning null for empty collection.
/// Exception is due to <see cref="Enumerable.First{TSource}(System.Collections.Generic.IEnumerable{TSource})"/> on an empty collection.
/// </summary>
[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void UnfortunateEducatedUseOfCarelessMethod()
{
container.Populate();
var elements = container.GetElementsInconvenientCareless();
if (elements == null)
{
Assert.Inconclusive();
}
Assert.IsNotNull(elements.First());
}
/// <summary>
/// Demonstrates the client code is bloated a bit, to compensate for implementation 'cleverness'.
/// We can throw away the nullness result, because we don't know if the operation succeeded or not anyway.
/// We are unfortunate to create a new instance of an empty collection.
/// We might have already had one inside the implementation,
/// but it have been discarded then in an effort to return null for empty collection.
/// </summary>
[Test]
public void EducatedUseOfCarefullMethod()
{
Assert.AreEqual(0, (container.GetElementsInconvenientCarefull() ?? Enumerable.Empty<Element>()).Count());
}
}
}
Tôi gọi đó là sai lầm hàng tỷ đô la của tôi Lúc đó, tôi đang thiết kế hệ thống loại toàn diện đầu tiên để tham khảo bằng ngôn ngữ hướng đối tượng. Mục tiêu của tôi là đảm bảo rằng tất cả việc sử dụng tài liệu tham khảo phải tuyệt đối an toàn, với việc kiểm tra được thực hiện tự động bởi trình biên dịch. Nhưng tôi không thể cưỡng lại sự cám dỗ để đưa vào một tài liệu tham khảo null, đơn giản vì nó rất dễ thực hiện. Điều này đã dẫn đến vô số lỗi, lỗ hổng và sự cố hệ thống, có thể gây ra hàng tỷ đô la đau đớn và thiệt hại trong bốn mươi năm qua. - Tony Hoare, nhà phát minh của ALGOL W.
Xem ở đây cho một cơn bão shit công phu null
nói chung. Tôi không đồng ý với tuyên bố đó undefined
là khác null
, nhưng nó vẫn đáng đọc. Và nó giải thích, tại sao bạn nên tránh null
tất cả và không chỉ trong trường hợp bạn đã hỏi. Bản chất là, null
trong bất kỳ ngôn ngữ nào là một trường hợp đặc biệt. Bạn phải nghĩ về null
một ngoại lệ. undefined
là khác nhau theo cách đó, mã xử lý hành vi không xác định trong hầu hết các trường hợp chỉ là một lỗi. C và hầu hết các ngôn ngữ khác cũng có hành vi không xác định nhưng hầu hết trong số họ không có định danh cho ngôn ngữ đó.
Từ góc độ quản lý sự phức tạp, mục tiêu kỹ thuật phần mềm chính, chúng tôi muốn tránh tuyên truyền không cần thiết phức tạp chu kỳ cho các máy khách của API. Trả lại null cho khách hàng cũng giống như trả lại cho họ chi phí phức tạp theo chu kỳ của một nhánh mã khác.
(Điều này tương ứng với gánh nặng kiểm tra đơn vị. Bạn sẽ cần phải viết một bài kiểm tra cho trường hợp trả về null, ngoài trường hợp trả về bộ sưu tập trống.)