使用Null Coalescing运算符的独特方法

时间:2008-11-10 18:21:27

标签: c# coding-style null conditional-operator null-coalescing-operator

我知道在C#中使用Null合并运算符的标准方法是设置默认值。

string nobody = null;
string somebody = "Bob Saget";
string anybody = "";

anybody = nobody   ?? "Mr. T"; // returns Mr. T
anybody = somebody ?? "Mr. T"; // returns "Bob Saget"

但是??可以用于什么呢?它不像三元运算符那样有用,除了比以下更简洁易读:

nobody = null;
anybody = nobody == null ? "Bob Saget" : nobody; // returns Bob Saget

所以考虑到甚至更少知道空合并运算符......

  • 您是否已将??用于其他内容?

  • 是否需要??,或者您应该使用三元运算符(那个 大多数人都很熟悉)

17 个答案:

答案 0 :(得分:199)

嗯,首先,它比标准三元组更容易链接:

string anybody = parm1 ?? localDefault ?? globalDefault;

VS

string anyboby = (parm1 != null) ? parm1 
               : ((localDefault != null) ? localDefault 
               : globalDefault);

如果null-possible对象不是变量,它也可以正常工作:

string anybody = Parameters["Name"] 
              ?? Settings["Name"] 
              ?? GlobalSetting["Name"];

VS。

string anybody = (Parameters["Name"] != null ? Parameters["Name"] 
                 : (Settings["Name"] != null) ? Settings["Name"]
                 :  GlobalSetting["Name"];

答案 1 :(得分:173)

我用它作为一个懒惰的负载单行:

public MyClass LazyProp
{
    get { return lazyField ?? (lazyField = new MyClass()); }
}

可读?自己决定。

答案 2 :(得分:51)

我发现它有两种“略微奇怪”的方式:

  • 作为在编写out例程时具有TryParse参数的替代方法(即,如果解析失败则返回空值)
  • 作为比较的“不知道”代表

后者需要更多信息。通常,当您创建与多个元素的比较时,您需要查看比较的第一部分(例如年龄)是否给出明确的答案,然后仅在第一部分没有帮助的情况下查看下一部分(例如名称)。使用空合并运算符意味着您可以编写非常简单的比较(无论是排序还是相等)。例如,在MiscUtil中使用几个辅助类:

public int Compare(Person p1, Person p2)
{
    return PartialComparer.Compare(p1.Age, p2.Age)
        ?? PartialComparer.Compare(p1.Name, p2.Name)
        ?? PartialComparer.Compare(p1.Salary, p2.Salary)
        ?? 0;
}

不可否认,我现在在MiscUtil中有ProjectionComparer,还有一些扩展,这使得这种事情变得更加容易 - 但它仍然很整洁。

在实现Equals开始时,可​​以检查参考相等(或无效)。

答案 3 :(得分:32)

另一个优点是三元运算符需要双重评估或临时变量。

考虑一下,例如:

string result = MyMethod() ?? "default value";

使用三元运算符时,您可以选择:

string result = (MyMethod () != null ? MyMethod () : "default value");

两次调用MyMethod,或者:

string methodResult = MyMethod ();
string result = (methodResult != null ? methodResult : "default value");

无论哪种方式,空合并运算符都更清晰,我想更高效。

答案 4 :(得分:20)

另一件需要考虑的事情是,coalesce运算符不会像三元一样调用属性的get方法两次。

因此,有些情况下您不应使用三元组,例如:

public class A
{
    var count = 0;
    private int? _prop = null;
    public int? Prop
    {
        get 
        {
            ++count;
            return _prop
        }
        set
        {
            _prop = value;
        }
    }
}

如果您使用:

var a = new A();
var b = a.Prop == null ? 0 : a.Prop;

getter将被调用两次,count变量将等于2,如果你使用:

var b = a.Prop ?? 0

count变量将等于1,应该如此。

答案 5 :(得分:14)

我发现??运算符的最大优点是您可以轻松地将可空值类型转换为非可空类型:

int? test = null;
var result = test ?? 0; // result is int, not int?

我经常在Linq查询中使用它:

Dictionary<int, int?> PurchaseQuantities;
// PurchaseQuantities populated via ASP .NET MVC form.
var totalPurchased = PurchaseQuantities.Sum(kvp => kvp.Value ?? 0);
// totalPurchased is int, not int?

答案 6 :(得分:9)

我已经习惯了?在我的IDataErrorInfo实现:

public string Error
{
    get
    {
        return this["Name"] ?? this["Address"] ?? this["Phone"];
    }
}

public string this[string columnName]
{
    get { ... }
}

如果任何单个属性处于“错误”状态,我会收到该错误,否则我将返回null。工作得很好。

答案 7 :(得分:7)

您可以使用null合并运算符使其更清晰一些,以处理未设置可选参数的情况:

public void Method(Arg arg = null)
{
    arg = arg ?? Arg.Default;
    ...

答案 8 :(得分:6)

我喜欢使用null coalesce运算符来延迟加载某些属性。

一个非常简单(和做作)的例子只是为了说明我的观点:

public class StackOverflow
{
    private IEnumerable<string> _definitions;
    public IEnumerable<string> Definitions
    {
        get
        {
            return _definitions ?? (
                _definitions = new List<string>
                {
                    "definition 1",
                    "definition 2",
                    "definition 3"
                }
            );
        }
    } 
}

答案 9 :(得分:5)

  

是吗?必要的,或者你应该只使用三元运算符(大多数人都熟悉)

实际上,我的经验是,很少有人熟悉三元运算符(或者更准确地说,条件运算符; ?:是“三元”的意思与{ {1}}是二进制的,或者||是一元的或二元的;它确实恰好是许多语言中唯一的三元运算符),所以至少在那个有限的样本中,你的语句就在那里失败。

另外,如前所述,当空合并运算符非常有用时,存在一种主要情况,即每当要计算的表达式具有任何副作用时。在这种情况下,不能使用条件运算符,而不是(a)引入临时变量,或(b)更改应用程序的实际逻辑。 (b)在任何情况下显然都不合适,虽然这是个人偏好,但我不喜欢用大量无关的,即使是短暂的变量来混淆声明范围,所以(a)也是如此特殊情况。

当然,如果您需要对结果进行多次检查,条件运算符或一组+块可能是该工作的工具。但是对于简单的“如果这是null,使用它,否则使用它”,空合并运算符if是完美的。

答案 10 :(得分:4)

唯一的问题是null-coalesce运算符没有检测到空字符串。


string result1 = string.empty ?? "dead code!";

string result2 = null ?? "coalesced!";

输出:

result1 = ""

result2 = coalesced!

我目前正在考虑重写?运营商要解决这个问题。将此内置到框架中肯定会很方便。

思想?

答案 11 :(得分:4)

我最近做的很多事情就是使用空合并来备份到as。例如:

object boxed = 4;
int i = (boxed as int?) ?? 99;

Console.WriteLine(i); // Prints 4

它也可用于备份可能各自失败的?.长链

int result = MyObj?.Prop?.Foo?.Val ?? 4;
string other = (MyObj?.Prop?.Foo?.Name as string)?.ToLower() ?? "not there";

答案 12 :(得分:3)

  

是吗?必要的,或者你应该只使用三元运算符(大多数人都熟悉)

你应该使用最能表达你意图的东西。由于一个空的合并运算符,使用它

另一方面,由于它是如此专业化,我认为它没有其他用途。我宁愿选择||运算符的适当重载,就像其他语言一样。这在语言设计中会更加简约。但是......好吧......

答案 13 :(得分:3)

酷!把我算作一个不知道空合并运算符的人 - 这是非常漂亮的东西。

我发现它比三元运算符更容易阅读。

我想到的第一个地方是将我的所有默认参数保存在一个地方。

public void someMethod( object parm2, ArrayList parm3 )
{ 
  someMethod( null, parm2, parm3 );
}
public void someMethod( string parm1, ArrayList parm3 )
{
  someMethod( parm1, null, parm3 );
}
public void someMethod( string parm1, object parm2, )
{
  someMethod( parm1, parm2, null );
}
public void someMethod( string parm1 )
{
  someMethod( parm1, null, null );
}
public void someMethod( object parm2 )
{
  someMethod( null, parm2, null );
}
public void someMethod( ArrayList parm3 )
{
  someMethod( null, null, parm3 );
}
public void someMethod( string parm1, object parm2, ArrayList parm3 )
{
  // Set your default parameters here rather than scattered through the above function overloads
  parm1 = parm1 ?? "Default User Name";
  parm2 = parm2 ?? GetCurrentUserObj();
  parm3 = parm3 ?? DefaultCustomerList;

  // Do the rest of the stuff here
}

答案 14 :(得分:1)

一个奇怪的用例,但我有一个方法,IDisposable对象可能作为arg传递(因此由父级处理),但也可以为null(因此应该创建和处置在当地方法)

要使用它,代码看起来像

Channel channel;
Authentication authentication;

if (entities == null)
{
    using (entities = Entities.GetEntities())
    {
        channel = entities.GetChannelById(googleShoppingChannelCredential.ChannelId);
        [...]
    }
}
else
{
    channel = entities.GetChannelById(googleShoppingChannelCredential.ChannelId);
    [...]
}

但是使用null coalesce变得更整洁

using (entities ?? Entities.GetEntities())
{
    channel = entities.GetChannelById(googleShoppingChannelCredential.ChannelId);
    [...]
}

答案 15 :(得分:0)

我曾经这样使用:

for (int i = 0; i < result.Count; i++)
            {
                object[] atom = result[i];

                atom[3] = atom[3] ?? 0;
                atom[4] = atom[4] != null ? "Test" : string.Empty;
                atom[5] = atom[5] ?? "";
                atom[6] = atom[6] ?? "";
                atom[7] = atom[7] ?? "";
                atom[8] = atom[8] ?? "";
                atom[9] = atom[9] ?? "";
                atom[10] = atom[10] ?? "";
                atom[12] = atom[12] ?? false; 
            }

答案 16 :(得分:-1)

Null-Coalescing运算符

此运算符检查变量,如果为null,则返回“ ??”运算符旁边的值,否则给出保存在变量中的值。

例如:-1

var name=”ATHUL”;
var result =name ?? “The name is null”
Console.WriteLine(result);

o / p:ATHUL

例如:-2

var name=null;
var result =name ?? “The name is null”
Console.WriteLine(result);

o / p:名称为空