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()); } }
}