10

Consider the following typical scenario:

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

I'm wondering what is thought of the following replacement using the ?? operator:

anObject = anObject ?? new AClass();

I'm not sure whether I should be using the second form. It seems like a nice shorthand, but the anObject = anObject construct at the beginning seems like it could be a bit of a code-smell.

Is this a reasonable thing to do, or is there a better shorthand that I am missing? Or maybe, "It's three lines, get over it!"?

4

4 回答 4

12

更新:

正如 OR Mapper 所指出的,问题在于自分配是否是代码气味。这是我书中的 6 和两个 3。赋值几乎不是一项昂贵的操作,无论如何,您可以在其他领域使用大多数数学运算符进行赋值。

我倾向于认为这不是代码气味。


我一直对惰性对象这样做(与您的示例略有不同):

return _myClass ?? (_myClass = new MyClass());

我认为这很好。奇怪的是我不倾向于使用Lazy<T>......不知道为什么,但我又不经常制作懒惰的物品。Lazy<T>它的意图更具表现力,例如,您可以读到该项目是延迟实例化的,但从技术上讲,它会object为现有项目增加另一个开销。我真的不担心这两种方式。

至于“克服它”,我认为这可能属于这一类。在这种情况下,我认为每个人都有自己的想法。

于 2013-09-06T13:59:14.117 回答
6

如果不将表达式的结果分配回同一个变量 - 例如a = b ?? new AClass();- 该模式很好,它可以用于“后备”新默认实例之类的东西:

private MyClass anObject;

// ...

(anObject ?? new MyClass()).DoSomething();

在这种情况下,不会存储新创建的对象以供以后重用。

当分配给同一个变量时,看起来你正在懒惰地初始化一些东西,在这种情况下 usingLazy<T>将是更具表现力的方式:

private Lazy<MyClass> anObject = new Lazy<MyClass>();

// ...

anObject.Value.DoSomething();

最迟将在最后一行执行时创建实例。

于 2013-09-06T14:01:29.283 回答
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(使用三元运算符)导致更多的堆栈操作。

除非您的计算机在时钟上运行,否则您的最终代码不会有任何差异,因此与许多事情一样 -执行您的代码最易读的那个

于 2013-09-06T14:05:12.577 回答
0

我在 WPF 视图模型中看到了几种模式(这是我看到这种模式最常用的地方):

方法1非懒惰:

public class ViewModel
{
    private readonly SomeValue _value;

    public ViewModel()
    {
        _value = new SomeValue();
    }

    public SomeValue Value { get { return _value; } }
}

方法一懒惰:

public class ViewModel
{
    private readonly Lazy<SomeValue> _value = new Lazy<SomeValue>(() => new SomeValue());

    public SomeValue Value { get { return _value.Value; } }
}

方法二:

public class ViewModel
{
    private SomeValue _value;

    public SomeValue Value { get { return _value ?? (_value = new SomeValue()); } } 
}

方法 1 和方法 2 之间的主要区别在于创建对象的时间和方式。方法 1 惰性使用Lazy(T)增加了创建的开销。只有大型对象才真正需要它,而 WPF 模型往往不会做太多繁重的工作。大对象可以在内部利用惰性来保持对象创建的合理性。

Method1 lazy 和 method 2 对于视图很大的时候非常有用。UI 应该保持响应,因此延迟对象创建可以提供更好的用户体验。当大型对象涉及复杂视图时,这将证明是必要的。

于 2013-09-06T14:13:39.673 回答