24

所以我对这个很感兴趣??运营商,但仍然无法使用它。当我做类似的事情时,我通常会考虑它:

var x = (someObject as someType).someMember;

如果 someObject 有效并且 someMember 为空,我可以这样做

var x = (someObject as someType).someMember ?? defaultValue;

但是当 someObject 为空时,我几乎总是会遇到问题,并且??并没有比我自己做空检查更干净。

大家找到什么用处了??在实际情况下?

4

13 回答 13

32

??运算符就像 SQL 中的coalesce方法,它为您获取第一个非空值。

var result = value1 ?? value2 ?? value3 ?? value4 ?? defaultValue;
于 2009-11-06T18:52:14.397 回答
27

我同意你的观点??运算符的用途通常有限——如果某些内容为空,则提供后备值很有用,但在您想继续深入研究有时的属性或方法时,它对于防止空引用异常没有用处 -空引用。

恕我直言,还需要什么?是一个“空引用”运算符,它允许您将长属性和/或方法链链接在一起,例如a.b().c.d().e,无需测试每个中间步骤的空值。Groovy语言有一个安全导航运算符,非常方便。

幸运的是,C# 团队似乎意识到了这个功能差距。请参阅此connect.microsoft.com 建议C# 团队向 C# 语言添加空取消引用运算符。

我们收到了很多与此类似的功能的请求。这 ”?。” 社区讨论中提到的版本最贴近我们的心——它允许您在每个“点”处测试 null,并与现有的 ?? 操作员:

a?.b?.c?? d

这意味着如果 a 、 ab 或 abc 中的任何一个为空,请使用 d 代替。

我们正在考虑将其用于未来的版本,但不会出现在 C# 4.0 中。

再次感谢,

Mads Torgersen,C# 语言 PM

如果您还希望在 C# 中使用此功能,请在连接站点上为建议添加您的投票!:-)

我用来解决缺少此功能的一种解决方法是使用扩展方法,如本博客文章中描述的那样,以允许这样的代码:

string s = h.MetaData
            .NullSafe(meta => meta.GetExtendedName())
            .NullSafe(s => s.Trim().ToLower())

如果 h、h.MetaData 或 h.MetaData.GetExtendedName() 为 null,则此代码返回h.MetaData.GetExtendedName().Trim().ToLower()或返回 null。我还扩展了它以检查 null 或空字符串,或者 null 或空集合。这是我用来定义这些扩展方法的代码:

public static class NullSafeExtensions
{
    /// <summary>
    /// Tests for null objects without re-computing values or assigning temporary variables.  Similar to 
    /// Groovy's "safe-dereference" operator .? which returns null if the object is null, and de-references
    /// if the object is not null.
    /// </summary>
    /// <typeparam name="TResult">resulting type of the expression</typeparam>
    /// <typeparam name="TCheck">type of object to check for null</typeparam>
    /// <param name="check">value to check for null</param>
    /// <param name="valueIfNotNull">delegate to compute if check is not null</param>
    /// <returns>null if check is null, the delegate's results otherwise</returns>
    public static TResult NullSafe<TCheck, TResult>(this TCheck check, Func<TCheck, TResult> valueIfNotNull)
        where TResult : class
        where TCheck : class
    {
        return check == null ? null : valueIfNotNull(check);
    }

    /// <summary>
    /// Tests for null/empty strings without re-computing values or assigning temporary variables
    /// </summary>
    /// <typeparam name="TResult">resulting type of the expression</typeparam>
    /// <param name="check">value to check for null</param>
    /// <param name="valueIfNotNullOrEmpty">delegate to compute non-null value</param>
    /// <returns>null if check is null, the delegate's results otherwise</returns>
    public static TResult CheckNullOrEmpty<TResult>(this string check, Func<string, TResult> valueIfNotNullOrEmpty)
        where TResult : class
    {
        return string.IsNullOrEmpty(check) ? null : valueIfNotNullOrEmpty(check);
    }
    /// <summary>
    /// Tests for null/empty collections without re-computing values or assigning temporary variables
    /// </summary>
    /// <typeparam name="TResult">resulting type of the expression</typeparam>
    /// <typeparam name="TCheck">type of collection or array to check</typeparam>
    /// <param name="check">value to check for null</param>
    /// <param name="valueIfNotNullOrEmpty">delegate to compute non-null value</param>
    /// <returns>null if check is null, the delegate's results otherwise</returns>
    public static TResult CheckNullOrEmpty<TCheck, TResult>(this TCheck check, Func<TCheck, TResult> valueIfNotNullOrEmpty)
        where TCheck : ICollection
        where TResult : class
    {
        return (check == null || check.Count == 0) ? null : valueIfNotNullOrEmpty(check);
    }
}
于 2009-11-06T19:01:39.677 回答
22

我通常将它用于字符串或可为空的类型。

string s = someString ?? "Default message";

比容易

   string s;
   if(someString == null)
     s = "Default Message";         
   else
     s = someString;

或者

  string s = someString != null ? someString : "Default Message";
于 2009-11-06T18:51:49.293 回答
12

我经常将它用于属性中延迟实例化的对象,例如

public MyObject MyProperty
{
    get
    {
        return this.myField ?? (this.myField = new MyObject());
    }
}

这利用了在 C# 中赋值是表达式的事实,因此您可以在同一语句中分配和使用表达式的结果(分配的值)。我仍然觉得这个 Code Pattern 有点脏,但它比替代方案(下图)更简洁,所以它几乎赢了。

public MyObject MyProperty
{
    get
    {
        if (this.myField == null)
        {
            this.myField = new MyObject();
        }

        return this.myField;
    }
}
于 2009-11-06T19:42:32.503 回答
8

就像一个 FYI 一样,三元运算符从空合并运算符生成不同的 IL 序列。第一个看起来像这样:

// string s = null;
// string y = s != null ? s : "Default";
    ldloc.0
    brtrue.s notnull1
    ldstr "Default"
    br.s isnull1
    notnull1: ldloc.0
    isnull1: stloc.1

后者看起来像这样:

// y = s ?? "Default";
    ldloc.0
    dup
    brtrue.s notnull2
    pop
    ldstr "Default"
    notnull2: stloc.1

基于此,我会说空合并运算符针对其目的进行了优化,而不仅仅是语法糖。

于 2009-11-06T20:59:21.427 回答
7

?? 实际上是一个空检查运算符。

您的代码遇到问题的原因是,如果“someObject 为空,则 .someMember 是空对象的属性。您必须首先检查 someObject 是否为空。

编辑:

我应该补充一点,是的,我确实找到了??非常有用,特别是在处理数据库输入时,尤其是来自 LINQ 的输入,但在其他情况下也是如此。我大量使用它。

于 2009-11-06T18:50:42.540 回答
3

我一直在使用它与 App.Config 的东西

string somethingsweet = ConfigurationManager.AppSettings["somesetting"] ?? "sugar";
于 2009-11-06T21:26:40.887 回答
2

最有用的是在处理可空类型时。

bool? whatabool;
//...
bool realBool = whatabool ?? false;

没有 ??您需要编写以下内容,这更加令人困惑。

bool realbool = false;
if (whatabool.HasValue)
    realbool = whatabool.Value;

我发现这个运算符对于可空类型非常有用,但除此之外,我发现自己并没有经常使用它。绝对是一个很酷的捷径。

于 2009-11-06T19:02:17.023 回答
1

在您的情况下,您可以将 DefaultObject 创建为静态属性并使用如下...

var x = (someObject as someType ?? someType.DefaultObject).someMember;

您的 DefaultObject 将返回一个静态的只读对象。

像 EventArgs.Empty、String.Empty 等...

于 2009-11-06T18:54:34.043 回答
1

我使用它来处理在正常操作下可以返回 null 的方法。

例如,假设我有一个容器类,它有一个 Get 方法,如果容器不包含键(或者 null 是一个有效的关联),则该方法返回 null。然后我可能会做这样的事情:

string GetStringRep(object key) {
    object o = container.Get(key) ?? "null";
    return o.ToString();
}

显然,这两个序列是等价的:

foo = bar != null ? bar : baz;
foo = bar ?? baz;

第二个只是更简洁。

于 2009-11-06T19:11:32.113 回答
1

用更紧凑的形式替换冗长的语法似乎是一个好主意。但通常它最终只是混淆了代码而没有真正获得任何东西。现代标准提倡更长和更具描述性的变量名称,并使用更详细的布局样式,以使代码更具可读性和可维护性。

如果我要训练我的大脑快速解析??,我需要找到一种情况,它显示出明显的优势——不能以其他方式完成的事情,或者可以节省大量打字的地方。

这是哪里??为我坠落。它很少有用,而且 ?: 和 if/else 是如此快速和可读的替代方案,以至于使用没有什么新东西的不同语法似乎没有什么意义。

a = b ?? C ???? e; 听起来很棒。但我从来不需要那样做!

令我恼火的是我想使用??,但每次我想“哦,也许我可以在这里使用??”时,我意识到我不想使用对象本身,而是它的成员.

名称=(用户==空)?“doe”:用户。姓氏;

不幸的是,?? 在这里没有帮助。

于 2009-11-06T19:29:44.523 回答
0

我遇到了与您完全相同的问题,但是在使用 LinqToXML 的空间中,文档中可能没有标签,因此使用??该标签上的属性合并是不可行的。

由于 C# 中没有全局变量之类的东西,所以一切都在一个类中,所以我发现和你一样,那是什么?大多是没用的。

甚至是一个简单的属性获取器,例如:

MyObject MyObj() { get { return _myObj ?? new MyObj(); } }

初始化程序可能更好地服务...

我想像在 SQL 中一样,它在 Linq 查询中会有些用处,但我现在无法真正推导出一个。

于 2009-11-06T18:55:18.323 回答
0

在将字符串解析为其他数据类型时,我经常使用 null-coalescing 运算符。我有一组扩展方法,它们包装 Int32.TryParse 之类的东西并返回一个可为空的 int,而不是将输出参数暴露给我的代码的其余部分。然后我可以解析一个字符串,如果解析失败,我可以在一行代码中提供一个默认值。

// ToNullableInt(), is an extension method on string that returns null
// if the input is null and returns null if Int32.TryParse fails.

int startPage = Request.QueryString["start"].ToNullableInt() ?? 0;
于 2009-11-06T21:13:36.010 回答