空合并运算符是不是有“对立面”? (……任何语言?)

Posted

技术标签:

【中文标题】空合并运算符是不是有“对立面”? (……任何语言?)【英文标题】:Is there an "opposite" to the null coalescing operator? (…in any language?)空合并运算符是否有“对立面”? (……任何语言?) 【发布时间】:2011-02-25 04:02:51 【问题描述】:

null 合并大致转换为 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 时才会被评估。

我认为这是几乎与空合并相反,有点混合了简洁的内联空检查,但我[几乎]确定C# 中没有这样的运算符。

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

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

【问题讨论】:

有些人在 C# 中要求 x?.y 之类的东西,但不存在这样的东西。 @Anthony 哦,那会很漂亮。谢谢。 在 c++ 中,这很容易表达为return x ? x.y : NULL。是的,将指针类型转换为布尔值! @Novelocrat 在 C# 中最让我恼火的事情之一是他们没有遵循 C 如果 if(anything) = true 除非它是 if(0, false, null) @Chris:这不是关于 C 的准确陈述。如果您有一个非标量变量(例如结构),则不能在条件中使用它。 【参考方案1】:

Groovy 中有 null-safe 解引用运算符 (?.)...我想这就是你所追求的。

(也叫safe navigation operator。)

例如:

homePostcode = person?.homeAddress?.postcode

如果personperson.homeAddressperson.homeAddress.postcode 为空,这将给出空值。

(现在是available in C# 6.0,但在早期版本中没有)

【讨论】:

Groovy 也有“Elvis 运算符”,它允许null 以外的默认值,例如:def bar = foo ?: "<null>" 我为 C# 5.0 提名了这个特性。我不知道也不关心 Groovy 到底是什么,但它足够直观,可以在任何语言中使用。 它现在被添加到 C# 6 中,万岁。 安全导航适用于发布的简单示例,但如果您打算在函数调用中使用非空值,您仍然需要使用三元运算符,例如:Decimal? post = pre == null ? null : SomeMethod( pre );。如果我能把它减少到“十进制?post = pre :: SomeMethod( pre );”,那就太好了 @Dai:考虑到您可能想要的排列数量,我对我们所拥有的感到非常满意。【参考方案2】:

更新:请求的功能已添加到 C# 6.0。以下 2010 年的原始答案应仅被视为具有历史意义。


我们考虑添加 ?。到 C# 4。它没有成功;这是一个“很高兴拥有”的功能,而不是“必须拥有”的功能。我们将再次考虑该语言的假设未来版本,但如果我是你,我不会屏住呼吸等待。随着时间的推移,它不太可能变得更加重要。 :-)

【讨论】:

是否易于实施功能会影响添加它的决定?我问这个是因为实现该运算符似乎是对语言的一个相当直接、简单的补充。我不是专家(只是一个热爱 C# 的应届毕业生),所以请纠正我! @nick:当然,实现词法分析器和解析器只需五分钟。然后您开始担心诸如“IntelliSense 引擎是否处理得好?”之类的问题。和“当您输入 ? 而不是 . 时,错误恢复系统如何处理它?”以及“。”有多少不同的事情。无论如何,在 C# 中的意思是,其中有多少值得拥有 ?在它之前,如果你弄错了错误消息应该是什么,这个功能需要多少测试(提示:很多。)我们将如何记录它并传达更改,以及...... @nick:回答你的问题——是的,实施成本是一个因素,但很小。在我们工作的级别上没有便宜的功能,只有或多或少昂贵的功能。使解析器在正确代码情况下工作的价值 5 美元的开发工作很容易变成价值数万美元的设计、实施、测试、文档和教育。 @EricLippert 我认为这将是主要的“值得拥有”之一,它使 C# 如此成功,并且完美地服务于使代码尽可能简洁和富有表现力的使命! 我希望看到这个添加,我真正想要的是 elvis 运算符:***.com/questions/2929836/…【参考方案3】:

如果您有一种特殊的短路布尔逻辑,您可以这样做(javascript 示例):

return x && x.y;

如果x 为空,则不会评估x.y

【讨论】:

除此之外还会在 0、"" 和 NaN 上短路,所以它不是 ?? 的反面。它与 || 相反。【参考方案4】:

添加这个作为答案感觉不错。

我猜 C# 中没有这样的事情的原因是,与合并运算符(仅对引用类型有效)不同,反向操作可以产生引用或值类型(即 class x 与成员int y - 因此很遗憾,它在许多情况下都无法使用。

然而,我并不是说我不想看到它!

该问题的一个潜在解决方案是让运算符自动将右侧的值类型表达式提升为可空值。但是你有一个问题,x.y 其中 y 是一个 int 实际上会返回一个 int? 这将是一个痛苦。

另一个可能更好的解决方案是,如果左侧的表达式为 null,则运算符返回右侧类型的默认值(即 null 或零)。但是,您在区分实际是从 x.y 读取零/空值还是由安全访问运算符提供的情况时会遇到问题。

【讨论】:

当我第一次阅读 OP 的问题时,这个确切的问题突然出现在我的脑海中,但我无法确定如何表达它。这是一个非常严重的问题。 +1 这不是一个严重的问题。只需使用int? 作为默认返回值,用户可以根据需要将其更改为int。例如,如果我想调用一些默认值为 -1 的方法 Lookup,以防我的引用之一是 nullfoo?.Bar?.Lookup(baz) ?? -1 如何让?. : 成为三元运算符,其中右侧需要与中间给出的相应成员的类型相同?【参考方案5】:

Delphi 有 :(而不是 .)运算符,它是 null 安全的。

他们正在考虑添加一个?。运算符到 C# 4.0 做同样的事情,但这得到了砧板。

In the meantime, there's IfNotNull() 哪一种抓痒痒。它肯定大于?。或 :,但它确实允许您组成一系列操作,如果其中一个成员为 null,则不会对您造成 NullReferenceException。

【讨论】:

你能举一个使用这个运算符的例子吗? ?. 是 C# 6.0 语言功能,是的,它完全符合 OP 在这里的要求。迟到总比没有好^^【参考方案6】:

在 Haskell 中,您可以使用 >> 运算符:

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

【讨论】:

【参考方案7】:

Haskell 有fmap,在这种情况下我认为它等同于Data.Maybe.map。 Haskell 纯粹是功能性的,所以你正在寻找的是

fmap select_y x

如果xNothing,则返回Nothing。如果xJust object,则返回Just (select_y object)。不如点表示法漂亮,但鉴于它是一种函数式语言,样式不同。

【讨论】:

【参考方案8】:

PowerShell 让您在 null 引用上引用属性(但不调用方法),如果实例为 null,它将返回 null。您可以在任何深度执行此操作。我曾希望 C# 4 的动态特性能够支持这一点,但它不支持。

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

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

它并不漂亮,但您可以添加一个扩展方法,该方法将按照您描述的方式运行。

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

【讨论】:

【参考方案9】:
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; 

我经常需要这个逻辑来处理字符串:

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"

【讨论】:

【参考方案10】:

在某处为您的类创建一个静态实例,并为成员提供所有正确的默认值。

例如:

z = new Thingy  y=null ;

然后代替你的

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

你可以写

return (x ?? z).y;

【讨论】:

我有时会使用这种技术(例如 (x ?? "").ToString()),但它只适用于 POD 和字符串等少数数据类型。【参考方案11】:

这是在 C# vNext 中添加的(Roslyn 支持的 C#,随 Visual Studio 2014 发布)。

它被称为 Null 传播,并在此处列出为完整的。 https://roslyn.codeplex.com/wikipage?title=Language%20Feature%20Status

这里也列出完整: https://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/3990187-add-operator-to-c

【讨论】:

【参考方案12】:

C# 6.0 和 Visual Basic 14 中引入了所谓的“空条件运算符”。 在许多情况下,它可以用作与 null-coalescing 运算符完全相反的情况:

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

【讨论】:

以上是关于空合并运算符是不是有“对立面”? (……任何语言?)的主要内容,如果未能解决你的问题,请参考以下文章

.NET Reflector 是不是无法正确反映空合并运算符?

可能重载空合并运算符?

空合并运算符的右结合如何表现?

带返回的空合并运算符 (??)

C#语法糖空合并运算符??和空合并赋值运算符 ??=

是否有 C# 空合并运算符的 Python 等效项?