使用??实例化空对象操作者

时间:2013-09-06 13:57:41

标签: c# .net

考虑以下典型情况:

if(anObject == null)
{
 anObject = new AClass();
}

我想知道以下替换使用的是什么?操作者:

anObject = anObject ?? new AClass();

我不确定我是否应该使用第二种形式。它似乎是一个很好的速记,但是开头的anObject = anObject构造看起来好像有点代码味道。

这是一件合理的事吗,还是有一个我错过的更好的速记?或许,“这是三行,克服它!”?

4 个答案:

答案 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应保持响应,因此推迟创建对象可提供更好的用户体验。当大型对象涉及复杂视图时,这将证明是必要的。