空合并运算符有一个“对立面”吗? (... 在任何语言中?)

空合并大致可以转换为 return x, unless it is null, in which case return y

我经常需要 return null if x is null, otherwise return x.y

我可以用 return x == null ? null : x.y;

不错,但是中间的 null总是让我感到困扰——它似乎是多余的。我更喜欢类似于 return x :: x.y;的内容,其中,只有在 ::之前的内容不是 null的情况下,才对 ::之后的内容进行计算。

我认为这是与 null 合并相反的 差不多,有点像与一个简洁的内联 null-check 混合在一起,但是我确信[ 差不多]在 C # 中没有这样的操作符。

还有其他语言有这样的操作符吗? 如果有,它叫什么?

(我知道我可以用 C # 为它编写一个方法; 我使用 return NullOrValue.of(x, () => x.y);,但是如果您有更好的方法,我也想看看。)

21312 次浏览

There's the null-safe dereferencing operator (?.) in Groovy... I think that's what you're after.

(It's also called the safe navigation operator.)

For example:

homePostcode = person?.homeAddress?.postcode

This will give null if person, person.homeAddress or person.homeAddress.postcode is null.

(This is now available in C# 6.0 but not in earlier versions)

Delphi has the : (rather than .) operator, which is null-safe.

They were thinking about adding a ?. operator to C# 4.0 to do the same, but that got the chopping block.

In the meantime, there's IfNotNull() which sort of scratches that itch. It's certainly larger than ?. or :, but it does let you compose a chain of operations that won't hork a NullReferenceException at you if one of the members is null.

If you've got a special kind of short-circuit boolean logic, you can do this (javascript example):

return x && x.y;

If x is null, then it won't evaluate x.y.

It just felt right to add this as an answer.

I guess the reason why there is no such thing in C# is because, unlike the coalescing operator (which is only valid for reference types), the reverse operation could yield either a reference or value type (i.e. class x with member int y - therefore it would unfortunately be unusable in many situations.

I'm not saying, however, that I wouldn't like to see it!

A potential solution to that problem would for the operator to automatically lift a value type expression on the right-hand-side to a nullable. But then you have the issue that x.y where y is an int will actually return an int? which would be a pain.

Another, probably better, solution would be for the operator to return the default value (i.e. null or zero) for the type on the right hand side if the expression on the left is null. But then you have issues distinguishing scenarios where a zero/null was actually read from x.y or whether it was supplied by the safe-access operator.

PowerShell let's you reference properties (but not call methods) on a null reference and it will return null if the instance is null. You can do this at any depth. I had hoped that C# 4's dynamic feature would support this but it does not.

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


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

It's not pretty but you could add an extension method that will function the way you describe.

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);

In Haskell, you can use the >> operator:

  • Nothing >> Nothing is Nothing
  • Nothing >> Just 1 is Nothing
  • Just 2 >> Nothing is Nothing
  • Just 2 >> Just 1 is Just 1

Haskell has fmap, which in this case I think is equivalent toData.Maybe.map. Haskell is purely functional, so what you are looking for would be

fmap select_y x

If x is Nothing, this returns Nothing. If x is Just object, this returns Just (select_y object). Not as pretty as dot notation, but given that it's a functional language, styles are different.

UPDATE: The requested feature was added to C# 6.0. The original answer from 2010 below should be considered of historical interest only.


We considered adding ?. to C# 4. It didn't make the cut; it's a "nice to have" feature, not a "gotta have" feature. We'll consider it again for hypothetical future versions of the language, but I wouldn't hold my breath waiting if I were you. It's not likely to get any more crucial as time goes on. :-)

Create a static instance of your class somewhere with all the right default values for the members.

For example:

z = new Thingy { y=null };

then instead of your

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

you can write

return (x ?? z).y;
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; }
}

I often need this logic for strings:

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"

This is being added in C# vNext (Roslyn powered C#, releases with Visual Studio 2014).

It is called Null propagation and is listed here as complete. https://roslyn.codeplex.com/wikipage?title=Language%20Feature%20Status

It is also listed here as complete: https://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/3990187-add-operator-to-c

The so called "null-conditional operator" has been introduced in C# 6.0 and in Visual Basic 14.
In many situations it can be used as the exact opposite of the null-coalescing operator:

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://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-conditional-operators