Có "đối lập" với toán tử liên kết null không? (…Bằng bất kỳ ngôn ngữ nào?)


92

null kết hợp dịch gần nghĩa là return x, unless it is null, in which case return y

Tôi thường cần return null if x is null, otherwise return x.y

tôi có thể sử dụng return x == null ? null : x.y;

Không tệ, nhưng điều đó nullở giữa luôn làm tôi khó chịu - có vẻ như thừa. Tôi muốn một cái gì đó giống như return x :: x.y;, trong đó những gì theo sau chỉ ::được đánh giá nếu những gì đứng trước nó thì không null.

Tôi thấy điều này gần như đối lập với kết hợp rỗng, kiểu trộn lẫn với kiểm tra null nội tuyến ngắn gọn, nhưng tôi [ gần như ] chắc chắn rằng không có toán tử nào như vậy trong C #.

Có ngôn ngữ nào khác có toán tử như vậy không? Nếu vậy, những gì nó được gọi là?

(Tôi biết rằng tôi có thể viết một phương thức cho nó trong C #; tôi sử dụng return NullOrValue.of(x, () => x.y);, nhưng nếu bạn có bất kỳ điều gì tốt hơn, tôi cũng muốn xem điều đó.)


Một số đã yêu cầu một cái gì đó như x? .Y trong C #, nhưng không có gì giống như vậy tồn tại.
Anthony Pegram

1
@Anthony Ồ, điều đó thật tuyệt. Cảm ơn.
Jay

2
Trong c ++, điều đó đủ dễ dàng để diễn đạt như return x ? x.y : NULL. Yay để chuyển đổi loại con trỏ thành boolean!
Phil Miller

1
@Novelocrat đó là một trong những điều mà kích thích tôi nhất trong C # là họ không đi theo C nếu đó if (bất cứ điều gì) = true trừ khi đó là if (0, false, null)
Chris Marisic

2
@Chris: đó không phải là một tuyên bố chính xác về C. Nếu bạn có một biến không vô hướng (chẳng hạn như struct), bạn không thể sử dụng biến đó trong một điều kiện.
Phil Miller

Câu trả lời:


62

Có một nhà điều hành hội nghị vô cùng an toàn (?.) Ở Groovy ... Tôi nghĩ đó là những gì bạn đang theo đuổi.

(Nó còn được gọi là toán tử điều hướng an toàn .)

Ví dụ:

homePostcode = person?.homeAddress?.postcode

Điều này sẽ cho null nếu person, person.homeAddresshoặc person.homeAddress.postcodelà null.

(Tính năng này hiện có sẵn trong C # 6.0 nhưng không có trong các phiên bản trước đó)


1
Groovy cũng có "toán tử Elvis", cho phép các giá trị mặc định khác với null, ví dụ:def bar = foo ?: "<null>"
Craig Stuntz

28
Tôi đề cử tính năng này cho C # 5.0. Tôi không biết hoặc không quan tâm Groovy thực sự là gì, nhưng điều này đủ trực quan để sử dụng trong bất kỳ ngôn ngữ nào.
György Andrasek

Tuy nhiên, nó đang được thêm vào C # 6 bây giờ, xin chào.
Jeff Mercado

1
Safe-chuyển hướng hoạt động cho các ví dụ tầm thường được đăng, nhưng bạn vẫn cần phải sử dụng các nhà điều hành ternary nếu bạn có ý định sử dụng các giá trị không null trong một cuộc gọi chức năng, ví dụ: Decimal? post = pre == null ? null : SomeMethod( pre );. Sẽ rất tuyệt nếu tôi có thể giảm nó xuống thành "Decimal? Post = pre :: SomeMethod (pre);"
Đại

@Dai: Với số lượng hoán vị mà bạn có thể muốn, tôi đủ hài lòng với những gì chúng tôi có.
Jon Skeet

36

Chúng tôi đã xem xét thêm?. đến C # 4. Nó không thực hiện cắt giảm; đó là một tính năng "tốt khi có", không phải là một tính năng "phải có". Chúng tôi sẽ xem xét nó một lần nữa cho các phiên bản giả định trong tương lai của ngôn ngữ, nhưng tôi sẽ không nín thở chờ đợi nếu tôi là bạn. Nó không có khả năng trở nên quan trọng hơn khi thời gian trôi qua. :-)


Việc dễ dàng triển khai một tính năng có ảnh hưởng đến quyết định thêm nó không? Tôi hỏi điều này bởi vì việc triển khai toán tử đó có vẻ giống như một sự bổ sung khá đơn giản, đơn giản cho ngôn ngữ. Tôi không phải là chuyên gia (chỉ là một sinh viên mới tốt nghiệp với tình yêu lớn dành cho C #), vì vậy hãy sửa cho tôi!
Nick Strupat,

14
@nick: Chắc chắn rồi, việc triển khai lexer và parser chỉ mất 5 phút. Sau đó, bạn bắt đầu lo lắng về những thứ như "công cụ IntelliSense có xử lý tốt không?" và "hệ thống khôi phục lỗi xử lý như thế nào khi bạn đã nhập dấu? nhưng không phải.?" và có bao nhiêu thứ khác nhau thực hiện "." nghĩa là trong C #, và có bao nhiêu trong số chúng xứng đáng có một? trước nó, và thông báo lỗi sẽ như thế nào nếu bạn hiểu sai và tính năng này sẽ cần kiểm tra bao nhiêu (gợi ý: rất nhiều.) Và chúng ta sẽ ghi lại nó và thông báo thay đổi như thế nào, và ...
Eric Lippert

3
@nick: Để trả lời câu hỏi của bạn - vâng, chi phí thực hiện là một yếu tố, nhưng là một yếu tố nhỏ. Không có tính năng rẻ ở cấp độ chúng tôi làm việc, chỉ có tính năng đắt hơn hoặc ít hơn. Các nhà phát triển trị giá năm đô la để làm cho trình phân tích cú pháp hoạt động trong trường hợp mã chính xác có thể dễ dàng biến thành hàng chục ngàn đô la cho thiết kế, triển khai, thử nghiệm, tài liệu và giáo dục.
Eric Lippert

11
@EricLippert Tôi nghĩ đây sẽ là một trong những "tuyệt vời khi có" CHÍNH, đã làm cho C # rất thành công và cũng sẽ phục vụ hoàn hảo sứ mệnh tạo mã ngắn gọn và diễn đạt nhất có thể!
Konstantin

Tôi muốn thấy điều này nói thêm, những gì tôi thực sự muốn là các nhà điều hành elvis: stackoverflow.com/questions/2929836/...
Chris Marisic

16

Nếu bạn có một loại logic boolean ngắn mạch đặc biệt, bạn có thể làm điều này (ví dụ javascript):

return x && x.y;

Nếu xlà null, thì nó sẽ không đánh giá x.y.


2
Ngoại trừ điều này cũng ngắn mạch trên 0, "" và NaN, vì vậy nó không ngược lại với ??. Nó đối lập với ||.
ritaj

7

Nó chỉ cảm thấy đúng khi thêm điều này như một câu trả lời.

Tôi đoán lý do tại sao không có điều đó trong C # là bởi vì, không giống như toán tử liên kết (chỉ hợp lệ cho các kiểu tham chiếu), hoạt động ngược lại có thể mang lại một kiểu tham chiếu hoặc giá trị (tức là class xvới thành viên int y- do đó nó sẽ không may là không sử dụng được trong nhiều tình huống.

Tuy nhiên, tôi không nói rằng tôi không muốn nhìn thấy nó!

Một giải pháp tiềm năng cho vấn đề đó là toán tử sẽ tự động nâng một biểu thức kiểu giá trị ở phía bên phải thành một giá trị có thể đặt được. Nhưng sau đó bạn có vấn đề rằng x.ynơi y là một int thực sự sẽ trả về một giá trị int?đó sẽ là một điều khó khăn.

Một giải pháp khác, có lẽ tốt hơn, sẽ là toán tử trả về giá trị mặc định (tức là null hoặc không) cho kiểu ở phía bên phải nếu biểu thức bên trái là null. Nhưng sau đó bạn gặp vấn đề trong việc phân biệt các tình huống trong đó số không / null thực sự được đọc từ đó x.yhoặc liệu nó có được cung cấp bởi toán tử truy cập an toàn hay không.


khi tôi lần đầu tiên đọc câu hỏi của OP, vấn đề chính xác này nảy ra trong đầu tôi, nhưng tôi không thể hiểu rõ làm thế nào để diễn đạt nó. Đó là một vấn đề rất nghiêm trọng. +1
rmeador

nó không phải là một vấn đề nghiêm trọng. Chỉ sử dụng int?làm giá trị trả về mặc định và người dùng có thể thay đổi nó thành giá trị int nếu muốn. Ví dụ nếu tôi muốn gọi một số phương pháp Lookupcó giá trị mặc định là -1 trong một trường hợp tài liệu tham khảo của tôi là null:foo?.Bar?.Lookup(baz) ?? -1
Qwertie

Làm thế nào về việc có ?. :một toán tử bậc ba, trong đó phía bên phải được yêu cầu cùng loại với thành viên thích hợp được đưa ra ở giữa?
supercat

6

Delphi có toán tử: (thay vì.), Là không an toàn.

Họ đã suy nghĩ về việc thêm một?. sang C # 4.0 để làm tương tự, nhưng điều đó có khối cắt.

Trong khi đó, có IfNotNull () giúp bạn gãi ngứa. Nó chắc chắn lớn hơn?. hoặc:, nhưng nó cho phép bạn soạn một chuỗi hoạt động sẽ không gây hại cho NullReferenceException nếu một trong các thành viên là null.


Bạn có thể cho một ví dụ về việc sử dụng toán tử này không?
Max Carroll vào

1
Đây ?.là một tính năng ngôn ngữ C # 6.0, và vâng, nó thực hiện chính xác những gì OP yêu cầu ở đây. Muộn còn hơn không ^^
T_D

3

Trong Haskell, bạn có thể sử dụng >>toán tử:

  • Nothing >> NothingNothing
  • Nothing >> Just 1Nothing
  • Just 2 >> NothingNothing
  • Just 2 >> Just 1Just 1

3

Haskell có fmap, mà trong trường hợp này tôi nghĩ là tương đương với Data.Maybe.map. Haskell hoàn toàn là chức năng, vì vậy những gì bạn đang tìm kiếm sẽ là

fmap select_y x

Nếu xNothing, lợi nhuận này Nothing. Nếu xJust object, lợi nhuận này Just (select_y object). Không đẹp như ký hiệu dấu chấm, nhưng vì nó là một ngôn ngữ chức năng, các phong cách khác nhau.


2

PowerShell cho phép bạn tham chiếu các thuộc tính (nhưng không gọi phương thức) trên một tham chiếu null và nó sẽ trả về null nếu thể hiện là null. Bạn có thể làm điều này ở bất kỳ độ sâu nào. Tôi đã hy vọng rằng tính năng động của C # 4 sẽ hỗ trợ điều này nhưng nó không.

$x = $null
$result = $x.y  # $result is null

$x = New-Object PSObject
$x | Add-Member NoteProperty y 'test'
$result = $x.y  # $result is 'test'

Nó không đẹp nhưng bạn có thể thêm một phương thức mở rộng sẽ hoạt động theo cách bạn mô tả.

public static TResult SafeGet<T, TResult>(this T obj, Func<T, TResult> selector) {
    if (obj == null) { return default(TResult); }
    else { return selector(obj); }
}

var myClass = new MyClass();
var result = myClass.SafeGet(x=>x.SomeProp);

2
public class ok<T> {
    T s;
    public static implicit operator ok<T>(T s) { return new ok<T> { s = s }; }
    public static implicit operator T(ok<T> _) { return _.s; }

    public static bool operator true(ok<T> _) { return _.s != null; }
    public static bool operator false(ok<T> _) { return _.s == null; }
    public static ok<T> operator &(ok<T> x, ok<T> y) { return y; }
}

Tôi thường cần logic này cho các chuỗi:

using ok = ok<string>;

...

string bob = null;
string joe = "joe";

string name = (ok)bob && bob.ToUpper();   // name == null, no error thrown
string boss = (ok)joe && joe.ToUpper();   // boss == "JOE"

1

Tạo một phiên bản tĩnh của lớp ở đâu đó với tất cả các giá trị mặc định phù hợp cho các thành viên.

Ví dụ:

z = new Thingy { y=null };

sau đó thay vì của bạn

return x != null ? x.y : null;

bạn có thể viết

return (x ?? z).y;

Đôi khi tôi sử dụng kỹ thuật đó (ví dụ: (x ?? "") .ToString ()) nhưng nó chỉ thực tế trên một số kiểu dữ liệu như POD và chuỗi.
Qwertie


1

Cái gọi là "toán tử điều kiện null" đã được giới thiệu trong C # 6.0 và trong Visual Basic 14.
Trong nhiều trường hợp, nó có thể được sử dụng hoàn toàn ngược lại với toán tử kết hợp null:

int? length = customers?.Length; // null if customers is null   
Customer first = customers?[0];  // null if customers is null  
int? count = customers?[0]?.Orders?.Count();  // null if customers, the first customer, or Orders is null

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-conditional-operators

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.