考虑以下典型情况:
if(anObject == null)
{
anObject = new AClass();
}
我想知道以下替换使用的是什么?操作者:
anObject = anObject ?? new AClass();
我不确定我是否应该使用第二种形式。它似乎是一个很好的速记,但是开头的anObject = anObject构造看起来好像有点代码味道。
这是一件合理的事吗,还是有一个我错过的更好的速记?或许,“这是三行,克服它!”?
答案 0 :(得分:12)
<强>更新
正如O. R. Mapper指出的那样,问题在于自我指派是否是一种代码嗅觉。这是我书中的6和2 3。分配几乎不是一项昂贵的操作,你可以在大多数数学运算符的其他方面进行分配。
我倾向于认为这不是代码味道。
<小时/> 我一直这样做懒惰的对象(你的例子略有不同):
return _myClass ?? (_myClass = new MyClass());
我认为没关系。奇怪的是,我不倾向于使用Lazy<T>
...不确定为什么,但是我再也不经常做懒物。 Lazy<T>
在其意图中更具表现力,因为您可以读到该项目是懒惰实例化的,但从技术上讲,它会为现有项目增加另一个object
开销。我真的不担心这两种方式。
至于“克服它”,我认为这可能属于那个类别。在这种情况下我认为每个人都属于自己。
答案 1 :(得分:6)
不将表达式的结果分配回同一个变量 - 例如a = b ?? new AClass();
- 该模式很好,它可以用作“后备”新默认实例:
private MyClass anObject;
// ...
(anObject ?? new MyClass()).DoSomething();
在这种情况下,不会存储新创建的对象以供以后重用。
当分配给同一个变量时,看起来你正懒惰地初始化一些东西,在这种情况下使用Lazy<T>
将是更具表现力的方式:
private Lazy<MyClass> anObject = new Lazy<MyClass>();
// ...
anObject.Value.DoSomething();
最后一行执行时最后会创建实例。
答案 2 :(得分:1)
void Main()
{
AClass anObject = null;
// option A
if (anObject == null)
{
anObject = new AClass();
}
// option B
anObject = anObject ? new AClass();
}
将选项A与选项B的优化IL代码进行比较:
Option A Option B Description
IL_0000: ldnull IL_0000: ldnull // Push a null reference on the stack.
IL_0001: stloc.0 // anObject IL_0001: stloc.0 // anObject // Pop a value from stack into local variable 0.
IL_0002: ldloc.0 // anObject IL_0002: ldloc.0 // anObject // Load local variable 0 onto stack
IL_0003: dup // Duplicate the value on the top of the stack.
IL_0003: brtrue.s IL_000B IL_0004: brtrue.s IL_000C // Branch to target if value is non-zero
IL_0006: pop // Pop value from the stack
IL_0005: newobj AClass..ctor IL_0007: newobj AClass..ctor // Allocate an uninitialized object or value type and call ctor
IL_000A: stloc.0 // anObject IL_000C: stloc.0 // anObject // Pop a value from stack into local variable 0.
正如您所看到的,两者之间的差别很小 - 选项B(使用三元运算符)会导致更多的堆栈操作。
除非您的计算机在发条上运行,否则您的最终代码不会有任何差异,因为许多内容都是 - 执行 代码中最具可读性的代码。
答案 3 :(得分:0)
我在WPF视图模型中看到过几种模式(我看到这种模式主要用于此模式):
方法1非懒惰:
public class ViewModel
{
private readonly SomeValue _value;
public ViewModel()
{
_value = new SomeValue();
}
public SomeValue Value { get { return _value; } }
}
方法1懒惰:
public class ViewModel
{
private readonly Lazy<SomeValue> _value = new Lazy<SomeValue>(() => new SomeValue());
public SomeValue Value { get { return _value.Value; } }
}
方法2:
public class ViewModel
{
private SomeValue _value;
public SomeValue Value { get { return _value ?? (_value = new SomeValue()); } }
}
方法1和方法2之间的主要区别在于对象的创建时间和方式。方法1懒惰使用Lazy(T)
,这增加了创建的开销。对于大型物体而言,它只是非常必要,而WPF模型往往不会做太多繁重的工作。大型对象可以利用内部的懒惰来保持对象的合理化。
方法1懒惰和方法2对于视图很大时非常有用。 UI应保持响应,因此推迟创建对象可提供更好的用户体验。当大型对象涉及复杂视图时,这将证明是必要的。