2011-08-08 31 views
6

Điều thực sự làm phiền tôi nhất về một số ngôn ngữ lập trình (ví dụ: C#, Javascript) là cố gắng truy cập thuộc tính của null gây ra lỗi hoặc ngoại lệ xảy ra.Tại sao cố gắng truy cập thuộc tính null gây ra ngoại lệ ở một số ngôn ngữ?

Ví dụ, trong đoạn mã sau,

foo = bar.baz; 

nếu thanh là null, C# sẽ ném một khó chịu NullReferenceException và phiên dịch javascript tôi sẽ phàn nàn với Unable to get value of the property 'baz': object is null or undefined.

tôi có thể hiểu được điều này, về mặt lý thuyết, nhưng trong mã thực tôi thường có các đối tượng hơi sâu, giống như

foo.bar.baz.qux 

và nếu bất kỳ một trong foo, bar, hoặc baz là null, mã của tôi là bị hỏng. :(Hơn nữa, nếu tôi đánh giá các biểu thức sau đây trong một giao diện điều khiển, có vẻ như là kết quả không phù hợp.

true.toString() //evaluates to "true" 
false.toString() //evaluates to "false" 
null.toString() //should evaluate to "null", but interpreter spits in your face instead 

tôi hoàn toàn khinh viết code để xử lý vấn đề này, bởi vì nó luôn tiết, mã mùi Các sau đây là những ví dụ không giả tạo, tôi nắm lấy những từ một trong những dự án của tôi (người đầu tiên là trong Javascript, thứ hai là trong C#):

if (!(solvedPuzzles && 
     solvedPuzzles[difficulty] && 
     solvedPuzzles[difficulty][index])) { 
     return undefined; 
    } 
return solvedPuzzles[difficulty][index].star 

if (context != null && 
    context.Request != null && 
    context.Request.Cookies != null && 
    context.Request.Cookies["SessionID"] != null) 
{ 
    SessionID = context.Request.Cookies["SessionID"].Value; 
} 
else 
{ 
    SessionID = null; 
} 

Mọi thứ sẽ dễ dàng hơn nhiều nếu toàn bộ biểu thức trả lại null nếu bất kỳ thuộc tính nào là rỗng. Các ví dụ mã ở trên có thể đơn giản hơn nhiều:

return solvedPuzzles[difficulty][index].star; 
    //Will return null if solvedPuzzles, difficulty, index, or star is null. 

SessionID = context.Request.Cookies["SessionID"].Value; 
    //SessionID will be null if the context, Request, Cookies, 
    //Cookies["SessionID"], or Value is null. 

Tôi đang thiếu cái gì? Tại sao những ngôn ngữ này không sử dụng hành vi này thay vì? Có khó để thực hiện vì một lý do nào đó không? Nó sẽ gây ra vấn đề mà tôi nhìn?

+0

Không có giá trị hợp lệ cho tất cả các giá trị đó trong chuỗi không? Nó dễ dàng hơn để không lưu trữ null trong đó ở nơi đầu tiên. Sau đó, bạn có thể tránh những kiểm tra hoàn toàn. –

+1

Bạn sẽ yêu thích [Mẫu đối tượng Null] (http://en.wikipedia.org/wiki/Null_Object_pattern) ... – fredoverflow

+3

Đó không phải là lỗi của ngôn ngữ mà bạn quyết định có một bộ sưu tập rỗng hơn là một bộ sưu tập trống. – canon

Trả lời

8

Nó có gây ra vấn đề mà tôi đang xem không?

Có - nó sẽ gây ra sự cố trong đó bạn có giá trị không null, nhưng do lỗi, bạn đã có giá trị null. Trong tình huống đó bạn muốn có ngoại lệ. Không im lặng và tiếp tục với dữ liệu xấu là một ý tưởng thực sự tồi tệ.

Một số ngôn ngữ (như Groovy) cung cấp toán tử dereference không an toàn có thể trợ giúp. Trong C# nó có thể giống như thế:

SessionID = context?.Request?.Cookies?["SessionID"]?.Value; 

Tôi tin rằng đội C# đã xem xét này trong quá khứ và thấy nó có vấn đề, nhưng điều đó không có nghĩa là họ sẽ không lại nó trong tương lai tất nhiên .

2

Và sau đó nếu đuôi cuối của cuộc gọi đó là một phương thức trả về bool, thì sao? Main.Child.Grandchild.IsEdit()bool IsEdit();

Tôi nghĩ sẽ tốt hơn nếu có một phiên bản "rỗng" trả về hành vi mặc định (được gọi là the null object pattern). Bằng cách đó, đối với những người mong đợi một null để chỉ ra một vấn đề, họ nhận được ngoại lệ của họ, nhưng nếu bạn biết một đối tượng mặc định là chấp nhận được, bạn có thể thực hiện điều này và không phải lo lắng về nó. Cả hai trường hợp sau đó được giải quyết.

Bạn có thể lấy tất cả các đối tượng "rỗng" từ INull, đặt chúng thành một băm so với tên lớp của chúng, sau đó tham khảo điều đó nếu một thành viên rỗng. Sau đó, bạn có thể kiểm soát việc thực thi "null" mặc định (nếu không, đối tượng là "null")

Ngoài ra, các đối tượng "null" có thể ném ngoại lệ nếu chúng được truy cập. chọn khi nào điều này sẽ ổn. Vì vậy, bạn có thể thực hiện "ngoại lệ đối tượng null" của riêng bạn.

Nếu họ đặt bất kỳ hỗ trợ ngôn ngữ nào, nó sẽ là dành cho tất cả mọi người, trên bảng cho đơn vị dịch hoặc đối tượng có vòng loại (tùy chọn cuối cùng là tùy chọn thích hợp), tại thời điểm đó, nó sẽ không ' t được mặc định, và bạn có thể đã thực hiện một ví dụ "null" mặc định.

+0

Vâng ... Làm cho nó 'bool? IsEdit() ': P –

+0

Tôi không biết nếu đó là tốt nhất bởi vì tôi không nghĩ rằng bạn có quyền kiểm soát tương tự. –

+0

Nó chắc chắn không tốt hơn. Tôi đã nói đùa. –

1

Tôi có thể đề nghị rằng nếu bạn phải kiểm tra xem có nhiều null, bạn đang cấu trúc mã của bạn kém. Rõ ràng, vì tôi không có mã của bạn, tôi không thể nói điều đó một cách dứt khoát. Tôi khuyên bạn nên chia mã của bạn thành các hàm nhỏ hơn. Bằng cách này, mã của bạn chỉ phải kiểm tra một hoặc có thể hai null tại một thời điểm, và có thể có nhiều quyền kiểm soát hơn những gì xảy ra nếu tại bất kỳ điểm nào đó là null.

+0

Tôi phải đồng ý với điều này- những bất biến của bạn không đủ mạnh. – Puppy

0

Nói chung foo.Baz có nghĩa là "trên ví dụ nếu foo, hãy thực thi thành viên baz". Đây có thể là một ngôn ngữ động, một thành viên ảo, hoặc bất cứ điều gì - nhưng chúng tôi đã thất bại ở bước đầu tiên trong tiền đề: không có ví dụ.

Những gì bạn được vuốt là một toán tử truy cập thành viên không an toàn. Khá hiếm, và thẳng thắn tôi không đồng ý với yêu cầu của bạn rằng điều này làm cho mã mùi (mặc dù tôi có thể tranh luận rằng bạn đang truy vấn xuống quá nhiều cấp độ trong một biểu thức duy nhất để được khỏe mạnh).

Tương tự như vậy, tôi không đồng ý rằng null.toString() phải trả về "không có giá trị" - IMO hoàn toàn bình thường cho việc này là không thành công. Điều trị truy cập trên một null là "bình thường" vốn đã sai IMO.

+0

Đối với các loại được cho là đại diện cho các thực thể hoặc chủ sở hữu dữ liệu có thể thay đổi, hoạt động trên 'null' không thực sự có ý nghĩa. Tuy nhiên, đối với các kiểu tham chiếu không thay đổi, điều duy nhất có thể là "sai" với việc cho phép các kiểu tạo 'default (someImmutableRefType) .SomeMember()' làm điều gì đó hữu ích sẽ là ràng buộc 'struct' trên' Nullable 'sẽ có nghĩa là sẽ không có cách "chuẩn" nào để xác định một giá trị tương đương nullable của loại đó. Nếu không, tôi thấy không có lý do nào một loại tham chiếu bất biến không thể hoạt động như một loại giá trị, bao gồm việc có giá trị mặc định có ý nghĩa. – supercat

0

Tôi đang thiếu cái gì? Tại sao những ngôn ngữ này không sử dụng hành vi này? Có khó để thực hiện vì một lý do nào đó không? Có phải nó gây ra các sự cố mà tôi đang xem không?

Khi một biến là null khi không mong muốn các ngôn ngữ mà bạn đề cập đến chọn thất bại đầu. Đây là một khái niệm trung tâm khi tạo các chương trình làm việc chính xác.

Tùy chọn sẽ ẩn các vấn đề về con trỏ null và chương trình sẽ chạy thêm một hàng, nhưng có thể sẽ bị lỗi sau hoặc thậm chí tệ hơn gây ra kết quả sai!

Ngoài ra khi cố khắc phục lỗi, sẽ dễ dàng khắc phục lỗi hơn nếu nó ném ngay NullReferenceException và trình gỡ lỗi hiển thị cho bạn dòng bắt đầu xem. Thay thế sẽ là triệu chứng mơ hồ - Khó khăn hơn nhiều để gỡ lỗi!

Rất phổ biến khi lập trình với null khi bạn không nên.Trong phần trên của bạn, hãy kiểm tra xem nulllà gì. Câu trả lời là nó không có nghĩa gì cả. Là một độc giả của mã tôi sẽ giả định các nhà văn quên một cái gì đó.

Giải pháp tốt nhất là giới thiệu một nhỏ, cái mà tôi gọi là "đối tượng rỗng" có thể được trả về thay vì null. Trong ví dụ của bạn có lẽ một lớp "NoDifficulty" hoặc "UnknownDifficulty" có thể được trả về để mang lại kết quả sau khi bạn kết thúc - mã sạch.

+0

Thành thực mà nói 'NullReferenceException' là không xa một triệu chứng mơ hồ, bởi vì' dòng x = null' có thể là hàng trăm dặm từ 'y.foo() 'dòng đó ném. –

+0

vâng, đúng vậy. Tin tôi đi, tôi đã thử cả hai. :) – vidstige

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