使用Null-Coalescing运算符实例化Null对象


12

请考虑以下典型情况:

if(myObject == null) {
    myObject = new myClass();
}

我想知道使用null-coalescing运算符进行以下替换的想法:

myObject = myObject ?? new myClass();

我不确定是否应该使用第二种形式。这似乎是一个不错的速记,但是myObject = myObject开始时的构造似乎有点代码气味。

这是合理的做法,还是我缺少更好的速记?或者,也许是“三行,克服它!”?

编辑:如上所述,也许将其称为典型场景是一种夸大其词的说法。我通常会在从具有子引用类型属性的数据库中检索一个实体时遇到这种情况,该子引用类型属性可能已填充也可能尚未填充:

myClass myObject = myClassService.getById(id);
myObject.myChildObject = myObject.myChildObject ?? new myChildClass();

15
新手认为这样的事情是“骇人的”,更有经验的开发人员只是将其称为“惯用的”。
布朗

如果对象看起来像值对象(惯用语言为“具有值语义”),MyClass则应提供其类型值的public static readonly成员Empty。参见String.EmptyMSDN
rwong


5
没有??=操作员吗?
2013年

1
我希望有@aviv!
罗伦·佩希特尔

Answers:


16

我一直都使用null合并运算符。我喜欢它的简洁。

我发现该运算符本质上与三元运算符(A?B:C)相似。在阅读它之前,需要进行一些练习,但是当您习惯了它之后,我觉得可读性比传统版本有所提高。

同样,您描述的情况只是操作员有用的一种情况。替换这样的结构也很方便:

if (value != null)
{
    return value;
}
else
{ 
    return otherValue;
}

要么

return value != null ? value : otherValue;

return value ?? otherValue;

绝对同意使用此运算符。但是,当我看到有关使用它的参考时,它通常类似于myObject = myObject2 ?? myObject3。我特别想知道的是使用运算符将​​对象设置为其自身,除非它为null。
grin0048

2
我认为在两种情况下都适用相同的推理。这是表达相同逻辑的一种更简洁的方法。

我记得曾经一次查看过这三种形式的IL。第一个和第二个是相同的,但是第三个null以一种比较的方式进行了优化。
Jesse C. Slicer,

2

我特别想知道的是使用运算符将​​对象设置为其自身,除非它为null。

?? operator被称为空合并运算符和用于定义为空值类型或引用类型的默认值。如果操作数不为null,则返回左侧的操作数;否则,返回左侧的操作数。否则返回正确的操作数。

myObject = myObject ?? new myObject(); -- instantiate default value of object

有关空合并运算符的更多详细信息和代码示例-MSDN文章

如果您检查more than nullable条件,作为替代方案,你可以使用三元操作符。

三元运算符

您还可以查看?:运算符。它称为三元运算符或条件运算符。条件运算符(?:)根据布尔表达式的值返回两个值之一。

可为空的类型可以包含一个值,也可以是未定义的。?? 运算符定义将可为空的类型分配给不可为空的类型时要返回的默认值。如果尝试在不使用??的情况下将可为空的值类型分配给不可为空的值类型 运算符,您将生成一个编译时错误。如果使用强制类型转换,并且当前未定义可为空的值类型,则将引发InvalidOperationException异常。

来自MSDN的代码示例-?:运算符(C#参考)

int? input = Convert.ToInt32(Console.ReadLine());
string classify;

// ?: conditional operator.
classify = (input.HasValue) ? ((input < 0) ? "negative" : "positive") : "undefined";

因此,从问题中拿出示例并应用条件运算符,我们将得到类似的信息myObject = (myObject == null) ? new myClass() : myObject。因此,除非我没有更好地使用条件运算符,否则在将对象设置为自身(如果它不为null时)时,它似乎与null运算符具有相同的“问题”,而且不够简洁。
grin0048

如果要检查多个条件(非null且不大于零),则条件运算符将执行此操作。但是,仅空检查将适用于?? 操作员。
Yusubov

2

我认为这种情况不是(或者至少应该是)典型的。

如果您不希望某个字段的默认值是null,请在字段初始化程序中进行设置:

myClass myObject = new myClass();

或者,如果初始化更为复杂,请在构造函数中进行设置。

如果myClass仅在实际需要时才创建(例如,因为创建需要很长时间),则可以使用Lazy<T>

Lazy<myClass> myObject = new Lazy<myClass>();

(这将调用默认构造函数。如果初始化更为复杂,myClass请将创建的lambda传递给Lazy<T>构造函数。)

要访问该值,请使用myObject.Value,如果这是您首次访问,它将调用初始化myObject.Value


IMO延迟创建仅在无法保证需要结果对象的情况下才有用,仅当我使用延迟创建时,才有兑现的派生结果,当某些事情发生变化时我将其重置,并且我知道我将需要大量相同的结果并且重新计算很昂贵。其他方面,我只是不想打扰null检票
棘轮怪胎

@ratchetfreak是的,这就是为什么我建议首先使用字段初始化程序的原因。
svick

还有其他情况。我现在正在看的是:第一遍设置例外情况。第二遍设置其他所有内容的默认设置。?? =将把线切成两半。
罗伦·佩希特尔

1

我特别喜欢??与可选的方法参数结合使用。我限制了对重载的需求,并确保事物具有价值。

public void DoSomething (MyClass thing1 = null) {
    thing1 = thing1 ?? new MyClass();
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.