Toán tử liên kết thuộc tính cho C #


9

Toán tử hợp nhất null trong c # cho phép bạn rút ngắn mã

  if (_mywidget == null)
     return new Widget();
  else
     return _mywidget;

Xuống tới:

  return _mywidget ?? new Widget();

Tôi tiếp tục thấy rằng một toán tử hữu ích mà tôi muốn có trong C # sẽ là một toán tử cho phép bạn trả về một thuộc tính của một đối tượng hoặc một số giá trị khác nếu đối tượng là null. Vì vậy, tôi muốn thay thế

  if (_mywidget == null)
     return 5;
  else
     return _mywidget.Length;

Với:

  return _mywidget.Length ??! 5;

Tôi không thể nghĩ rằng phải có một số lý do để nhà điều hành này không tồn tại. Có phải là một mùi mã? Có cách nào tốt hơn để viết này? (Tôi biết về mẫu đối tượng null nhưng dường như quá mức cần thiết để sử dụng nó để thay thế bốn dòng mã này.)


1
Toán tử có điều kiện đủ ở đây?
Anon.

1
Ai đó đã viết một cái gì đó cho phép bạn làm một cái gì đó như thế này: chuỗi location = worker.office.address.location ?? "Không xác định"; . Điều này sẽ đặt vị trí thành "Không xác định" khi một trong các đối tượng ( nhân viên , văn phòng , địa chỉ hoặc địa điểm ) là null. Thật không may, tôi không nhớ ai đã viết nó hoặc nơi anh ấy đăng nó. Nếu tôi tìm thấy nó một lần nữa, tôi sẽ đăng nó ở đây!
Kristof Claes

1
Câu hỏi này sẽ nhận được nhiều lực kéo hơn trên StackOverflow.
Công việc

2
??!là một toán tử trong C ++. :-)
James McNellis

3
Xin chào năm 2011, chỉ cần gọi để nói rằng c # đang có được một nhà điều hành an toàn
Nathan Cooper

Câu trả lời:


5

Tôi rất muốn một tính năng ngôn ngữ C # cho phép tôi thực hiện x.Prop.SomeOtherProp.ThirdProp một cách an toàn mà không phải kiểm tra từng tính năng đó cho null. Trong một cơ sở mã hóa mà tôi phải thực hiện rất nhiều kiểu "truy tìm thuộc tính sâu" đó, tôi đã viết một phương thức mở rộng có tên là Điều hướng nuốt NullReferenceExceptions và thay vào đó trả về giá trị mặc định. Vì vậy, tôi có thể làm một cái gì đó như thế này:

var propVal = myThing.Navigate(x => x.PropOne.PropTwo.PropThree.PropFour, defaultValue);

Nhược điểm của việc này là nó có mùi mã khác: nuốt ngoại lệ. Nếu bạn muốn làm một cái gì đó như "đúng" này, bạn có thể lấy lamdba làm biểu thức và sửa đổi biểu thức trên bay để thêm kiểm tra null xung quanh mỗi người truy cập thuộc tính. Tôi đã đi "nhanh và bẩn" và không thực hiện theo cách "tốt hơn" này. Nhưng có lẽ khi tôi có một số chu kỳ suy nghĩ rảnh rỗi, tôi sẽ xem lại nó và đăng nó ở đâu đó.

Để trả lời trực tiếp hơn câu hỏi của bạn, tôi đoán lý do đây không phải là một tính năng là vì chi phí triển khai tính năng vượt quá lợi ích từ việc có tính năng này. Nhóm C # phải chọn và chọn các tính năng cần tập trung vào và tính năng này chưa nổi lên hàng đầu. Chỉ là một phỏng đoán mặc dù tôi không có bất kỳ thông tin nội bộ nào.


1
Chính xác là những gì tôi nghĩ, nhưng hiệu suất của cách an toàn sẽ khá tệ, tôi đoán (vì có nhiều Expression.Compile ()) ... Thật tệ là nó không được triển khai trong C # (một cái gì đó như Obj.?PropA.?Prop1)
Guillaume86

x.PropOne.PropTwo.PropThree.PropFour là một lựa chọn thiết kế kém vì nó vi phạm Luật Demeter. Thiết kế lại các phương thức / lớp của bạn để bạn không cần sử dụng phương thức này.
Justin Shield

Vô thức tránh truy cập tài sản sâu dưới ánh sáng của Luật Demeter cũng nguy hiểm như việc bình thường hóa một cơ sở dữ liệu. Bạn có thể đi quá xa.
Mir

3
Trong C # 6.0, điều này có thể thực hiện được bằng cách sử dụng Toán tử Null-có điều kiện (còn gọi là toán tử Elvis) msdn.microsoft.com/en-us/magazine/dn802602.aspx
victorvartan

15

Tôi tin rằng bạn sẽ có thể làm điều này với C # 6 khá đơn giản ngay bây giờ:

return _mywidget?.Length ?? 5;

Lưu ý toán tử có điều kiện null ?.ở phía bên trái. _mywidget?.Lengthtrả về null nếu _mywidgetlà null.

Tuy nhiên, một toán tử ternary đơn giản có thể dễ đọc hơn, như những người khác đã đề xuất.


9

Đó thực sự chỉ là suy đoán tại sao họ không làm điều đó. Rốt cuộc, tôi không tin ?? là trong phiên bản C # đầu tiên.

Dù sao, tôi sẽ chỉ sử dụng toán tử có điều kiện và gọi nó là một ngày:

return (_mywidget != null) ? _mywidget.Length : 5;

Các ?? chỉ là một lối tắt đến toán tử có điều kiện.


5
Cá nhân, điều này cũng tệ (imho) như có một nhà điều hành để làm điều này ở nơi đầu tiên. Bạn muốn một cái gì đó từ một đối tượng ... đối tượng đó có thể không có ở đó ... vì vậy hãy cung cấp 5như câu trả lời trong trường hợp nó không có ở đó. Bạn cần phải xem xét thiết kế của bạn trước khi bạn bắt đầu yêu cầu các nhà khai thác đặc biệt.
Moo-Juice

1
@ Moo-Juice Tôi chỉ đang tưởng tượng người duy trì mã này, 'Tại sao điều này mang lại cho tôi 5? Hừm. Gì?!'
msarchet

4

Nó trông giống như toán tử ternary:

return (_mywidget != NULL) ? _mywidget : new Widget();

3

Cá nhân, tôi nghĩ rằng nó là xuống khả năng đọc. Trong ví dụ đầu tiên, ??bản dịch khá độc đáo là "Bạn có ở đó không? Không, hãy làm điều này".

Trong ví dụ thứ hai, ??!rõ ràng là WTF và không có nghĩa gì với lập trình viên .... đối tượng không tồn tại, vì vậy tôi không thể nhận được tại khách sạn nên tôi sẽ trả lại 5. Điều đó có nghĩa là gì? 5 là gì? Làm thế nào để chúng ta đi đến kết luận rằng 5 là một giá trị tốt?

Nói tóm lại, ví dụ đầu tiên có ý nghĩa ... không có, mới nó. Trong lần thứ hai, nó mở để tranh luận.


2
Chà, bạn phải bỏ qua thực tế đó chứ ??! không tồn tại Hãy tưởng tượng nếu ??! là phổ biến, và nếu nó được sử dụng, sau đó đưa ra quyết định dựa trên điều đó.
whatsisname

3
Điều đó làm tôi nhớ đến cái gọi là "toán tử WTF" trong C ++ (cái này thực sự hoạt động : (foo() != ERROR)??!??! cerr << "Error occurred" << endl;.)
Tamás Szelei

@whatsisname, nó phản ánh nhiều hơn về thực tế rằng nó phù hợp với quyết định được đưa ra. Để tạo ra một đối tượng bởi vì nó không có ý nghĩa. Để gán một giá trị tùy ý không có ý nghĩa gì đối với người đọc bởi vì bạn không thể có được tài sản của một thứ gì đó, thực sự là một kịch bản WTF.
Moo-Juice

Tôi nghĩ rằng bạn đang bị cuốn vào ví dụ của tôi. Có thể 0 tốt hơn 5. Có thể tài sản là một đối tượng vì vậy nếu _mywidget là null, bạn muốn trả về một foodle mới (). Tôi hoàn toàn không đồng ý rằng ?? dễ đọc hơn ??! mặc dù :)
Ben Fulton

@Ben, Mặc dù tôi đồng ý rằng việc tập trung vào chính nhà điều hành không cho vay quá nhiều vào câu hỏi của bạn, nhưng tôi đã vẽ song song với nhận thức của lập trình viên và sự tùy tiện đó 5. Tôi thực sự nghĩ rằng có nhiều vấn đề hơn là bạn phải làm một cái gì đó như thế này, trong khi trong ví dụ đầu tiên của bạn, nhu cầu này khá rõ ràng, đặc biệt là trong những thứ như trình quản lý bộ đệm.
Moo-Juice

2

Tôi nghĩ mọi người đang bị cuốn vào "5" khi bạn quay trở lại ... có lẽ 0 sẽ tốt hơn :)

Dù sao, tôi nghĩ vấn đề là nó ??!không thực sự là một toán tử độc lập. Xem xét điều này có nghĩa là gì:

var s = myString ??! "";

Trong trường hợp đó, nó không có nghĩa gì: nó chỉ có ý nghĩa nếu toán hạng bên trái là một bộ truy cập thuộc tính. Hoặc những gì về điều này:

var s = Foo(myWidget.Length) ??! 0;

Có một người truy cập tài sản ở đó, nhưng tôi vẫn không nghĩ nó có ý nghĩa (nếu myWidgetnull, điều đó có nghĩa là chúng ta không gọi Foo()gì cả?), Hay đây chỉ là một lỗi?

Tôi nghĩ vấn đề là nó không phù hợp với ngôn ngữ một cách tự nhiên ??.


1

Ai đó đã tạo ra một lớp tiện ích sẽ làm điều này cho bạn. Nhưng tôi không thể tìm thấy nó. Những gì tôi đã tìm thấy là một cái gì đó tương tự trên Diễn đàn MSDN (nhìn vào câu trả lời thứ hai).

Với một chút công việc, bạn có thể mở rộng nó để đánh giá các cuộc gọi phương thức và các biểu thức khác mà mẫu không hỗ trợ. Bạn cũng có thể mở rộng nó để chấp nhận một giá trị mặc định.


1

Tôi hiểu bạn đến từ đâu. Có vẻ như tất cả mọi người đang quấn quanh sợi trục với ví dụ bạn cung cấp. Mặc dù tôi đồng ý rằng các số ma thuật là một ý tưởng tồi, nhưng sẽ có việc sử dụng tương đương với toán tử coallescing null cho các thuộc tính nhất định.

Ví dụ: bạn có các đối tượng được liên kết với bản đồ và bạn muốn chúng ở độ cao phù hợp. Bản đồ DTED có thể có lỗ hổng trong dữ liệu của họ, do đó có thể có giá trị null, cũng như các giá trị trong phạm vi của khoảng từ -100 đến ~ 8900 mét. Bạn có thể muốn một cái gì đó dọc theo dòng:

mapObject.Altitude = mapObject.Coordinates.DtedAltitude ?? DEFAULT_ALTITUDE;

Toán tử coallescing null trong trường hợp này sẽ xác định độ cao mặc định nếu các tọa độ chưa được đặt hoặc đối tượng Tọa độ không thể tải dữ liệu DTED cho vị trí đó.

Tôi thấy điều này rất có giá trị, nhưng suy đoán của tôi về lý do tại sao nó không được thực hiện chỉ giới hạn ở độ phức tạp của trình biên dịch. Có thể có một số trường hợp hành vi sẽ không như dự đoán.


1

Khi không xử lý được lồng nhau sâu, tôi đã sử dụng cái này (trước khi toán tử hợp nhất null được giới thiệu trong C # 6). Đối với tôi nó khá rõ ràng, nhưng có lẽ vì tôi đã quen với nó nên những người khác có thể thấy nó khó hiểu.

return ( _mywidget ?? new MyWidget() {length = defaultLength}).Length;

Tất nhiên, điều này không phải lúc nào cũng có thể áp dụng được, vì đôi khi tài sản bạn cần truy cập không thể được đặt trực tiếp hoặc khi việc xây dựng đối tượng tốn kém, trong số các lý do khác.

Một cách khác là sử dụng mẫu NullObject . Bạn định nghĩa một thể hiện tĩnh duy nhất của lớp với các giá trị mặc định hợp lý và sử dụng nó để thay thế.

return ( _mywidget ?? myWidget.NullInstance).Length;

0

Số ma thuật thường là một mùi hôi. Nếu 5 là một số tùy ý, thì tốt hơn là nên đánh vần nó để nó được ghi lại rõ ràng hơn. Theo tôi, một ngoại lệ sẽ là nếu bạn có một tập hợp các giá trị đóng vai trò là giá trị mặc định trong một ngữ cảnh cụ thể.

Nếu bạn có một tập hợp các giá trị mặc định cho một đối tượng, bạn có thể phân lớp nó để tạo một cá thể singleton mặc định.

Một cái gì đó như: return (myobj ?? default_obj). Chiều dài


0

Thuộc tính độ dài thường là một loại giá trị vì vậy nó không thể là null, vì vậy toán tử liên kết null không có ý nghĩa ở đây.

Nhưng bạn có thể làm điều đó với một thuộc tính là loại tham chiếu, ví dụ: var window = App.Current.MainWindow ?? new Window();


Ví dụ đang cố gắng xem xét những gì sẽ xảy ra trong tình huống của bạn nếu Hiện tại là null. Ví dụ của bạn sẽ sụp đổ ... nhưng có một toán tử có thể kết hợp bất cứ nơi nào trong chuỗi định hướng sai sẽ rất hữu ích.
Mir

-1

Tôi đã thấy ai đó có ý tưởng tương tự trước đây nhưng hầu hết thời gian bạn cũng muốn gán giá trị mới vào trường null, đại loại như:

return _obj ?? (_obj = new Class());

Vì vậy, ý tưởng là kết hợp ??=phân công thành:

return _obj ??= new Class();

Tôi nghĩ rằng điều này có ý nghĩa hơn là sử dụng dấu chấm than.

Ý tưởng này không phải của tôi nhưng tôi thực sự thích nó :)


1
Bạn có phải là nạn nhân của tấm gương nghèo? Ví dụ của bạn có thể được rút ngắn để return _obj ?? new Class();không cần ??=ở đây.
Matt Ellen

@Matt Ellen bạn đã hiểu sai. Nhiệm vụ được dự định . Tôi đang đề xuất một phương án khác. Nó không hoàn toàn chính xác như những gì OP yêu cầu, nhưng tôi tin rằng nó sẽ hữu ích như vậy.
chakrit

2
Tại sao bạn muốn chuyển nhượng nếu bạn sắp trả về một giá trị?
Matt Ellen

lười khởi tạo?
chakrit
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.