C# 用??操作人员

C# 用??操作人员,c#,.net,C#,.net,考虑以下典型场景: if(anObject == null) { anObject = new AClass(); } 我想知道下面的替换使用??接线员: anObject = anObject ?? new AClass(); 我不确定是否应该使用第二种形式。这似乎是一个很好的简写,但开头的anObject=anObject构造似乎有点代码味道 这是合理的做法,还是我缺少了更好的速记方法?或者,“这是三句话,别再提了!”?更新: 正如O.R.Mapper所指出的,问题在于自我分配是否是一

考虑以下典型场景:

if(anObject == null)
{
 anObject = new AClass();
}
我想知道下面的替换使用??接线员:

anObject = anObject ?? new AClass();
我不确定是否应该使用第二种形式。这似乎是一个很好的简写,但开头的anObject=anObject构造似乎有点代码味道


这是合理的做法,还是我缺少了更好的速记方法?或者,“这是三句话,别再提了!”?

更新:

正如O.R.Mapper所指出的,问题在于自我分配是否是一种代码气味。这是我书中的6和2个3。赋值很难是一个昂贵的运算,而且在其他领域,至少在大多数数学运算符中都是如此

我倾向于认为这不是一种代码气味。
我一直在为惰性对象这样做(与您的示例略有不同):

我觉得很好。奇怪的是,我不倾向于使用
Lazy
。。。不知道为什么,不过我也不经常做懒散的东西
Lazy
的意图更具表现力,如中所示,您可以看到该项是延迟实例化的,但从技术上讲,它会向现有项添加另一个
对象
开销。我真的不担心,真的


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

而不将表达式的结果赋回同一个变量-例如
a=b??新AClass()-该模式很好,可用于“回退”新默认实例:

private MyClass anObject;

// ...

(anObject ?? new MyClass()).DoSomething();
在这种情况下,不会存储新创建的对象以供以后重用

当分配给同一个变量时,看起来您是在懒洋洋地初始化某些内容,在这种情况下,使用将是更具表现力的方式:

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

// ...

anObject.Value.DoSomething();
private Lazy anObject=new Lazy();
// ...
anObject.Value.DoSomething();
实例最迟将在最后一行执行时创建

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(使用三元运算符)之间的差异非常小,导致了更多的堆栈操作


除非您的计算机在时钟上运行,否则您在最终代码中看不到任何差异,因为在许多事情上-做代码可读性最好的事情

我在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; } }
}
方法1和方法2之间的主要区别在于创建对象的时间和方式。方法1 lazy使用
lazy(T)
,这增加了创建的开销。它只对大型对象才是真正必要的,WPF模型通常不会进行太多的重载。大型对象可以在内部利用惰性来保持对象创建的合理性


方法1和方法2在视图较大时非常有用。UI应该保持响应性,因此延迟对象创建可以提供更好的用户体验。当大型对象涉及复杂视图时,这将被证明是必要的。

如果这是一种代码气味,那么首先就不会引入它。第二种形式更简洁,应该优先考虑。@Jeroenvanevel:问题不在于
??
运算符是否为代码气味,而在于它在表达式中的使用是否是代码气味。实际上,这是四行…我一直不明白为什么没有
x???=y运算符,相当于
x=x??y。有
+=
-=
*=
,等等……但是在开头没有
anObject=anObject
。您有一个赋值,左边是
anObject
,左边是
anObject??new AClass()
右侧。谢谢一个非常好的主意,我会在12分钟后标记答案,我现在无法回答。谢谢。我不认为这些都是代码气味。在我上面的评论中,我只想指出,是否引入了“it”-它是
操作符-是不相关的,因为问题不在于操作符本身是否有代码气味,而在于问题中所示的使用它的特殊方式。@O.R.Mapper抱歉,我会重述,我想说的是,你强调了这个问题是关于自我分配的,以及这是否是一种代码气味。提示:如果类的构造函数可能引发异常,请使用接受
Func
委托(lambda)的
Lazy
构造函数。这将缓存并重新抛出异常,其中无参数构造函数将抛出TargetInvocationException。(更好-避免抛出构造函数的异常。)虽然我同意
Lazy
更具表现力,而且显然是为完成任务而设计的,但当我读到“首选方法”时,我有点畏缩,好像大家都同意一样。@AdamHouldsworth:你说得对,我改进了答案中的词语选择。+1用于更正。我通常倾向于避开“最佳实践”问题,但我觉得这个问题比大多数问题更无害。如果你违背了“规范”,你会受到很多的抨击,-PSo这是不是说联合操作符产生了更简洁的IL?不知道这件事没有解释是怎么回答的,更不用说它是怎么被投票两次了。。。
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()); } } 
}