6

我习惯把代码写成(只是一个例子)

Request.QueryString["xxxx"] != null

最近有人说

null != Request.QueryString["xxxx"]

提供更好的性能。

我很想知道它是否真的带来了任何差异,如果是的话,如何?

注意~以上只是一个例子。笼统地说

无论

Constant [Operator] Actual Value (e.g. 1 == Convert.ToInt32(textbox1.text))

好于

Actual Value [Operator] Constant (e.g. Convert.ToInt32(textbox1.text) == 1)

谢谢

4

4 回答 4

15

什么

这是一种用于具有许多隐式类型转换的语言的编码风格(由于他的OSV词序,友好地命名为来自星球大战角色的 Yoda )。有时它甚至被项目指南强制执行和要求,以防止因拼写错误而产生的某种错误。不同的语言对这种风格有不同的变体和扩展,但我会将这个答案限制为 C 和 C#。

为什么是

例如,在 C 中,您可以编写:

int a = CalculateValue();
if (a = CalculateAnotherValue()) {
    /* Do something */
}

此代码将分配给a从返回的值,CalculateValue()然后它将用结果覆盖该值,CalculateAnotherValue()如果它不为零,那么它将执行代码。可能这不是打算做的,if表达式中的赋值是一个错误。从您的示例中,NULL您可能有:

if (pBuffer = NULL)

同样,这可能不是您想要的(这是一个很常见的错误),您将 NULL 分配给指针并且条件将始终为 false。如果你写:

if (NULL = pBuffer)

它不会编译(因为你不能为文字赋值)并且你会得到一个编译时错误。

这种编译时检查并不是使用这种编码风格的唯一原因,看看这个 C#(非常常见)代码:

if (text != null && text.Equals("something", StringComparison.InvariantCulture)
    DoSomething();

它可以签约为:

if ("something".Equals(text, StringComparison.InvariantCulture))
    DoSomething();

为什么不

在 C#中通常没关系。这是继承自 C/C++的一种做法。因为在 C# 中表达式不会自动转换为bool以下代码将无法编译:

if (Request.QueryString["PartnerID"] = null)

然后这种做法在 C# 中是没有用的(@IlianPinzon 在评论中指出的例外),与性能无关,它只是用来避免此类错误。

关于为什么是部分中的最后一个示例,问题在于可读性,写"something".Equals(text)起来就像说“如果那个人开心的话”而不是“如果这个人很开心”。

表现

从这些功能开始:

static bool TestRight(object value)
{ return value == null; }

static bool TestLeft(object value)
{ return null == value; }

他们产生以下IL:

.maxstack 2
.locals init ([0] bool CS$1$0000)
L_0000: nop 
L_0001: ldnull 
L_0002: ldarg.0 
L_0003: ceq 
L_0005: stloc.0 
L_0006: br.s L_0008
L_0008: ldloc.0 
L_0009: ret 

唯一的区别在于 L_0001 和 L_0002 行,它们只是交换了,但其操作数的顺序不会改变ceq. 即使您覆盖Equals()方法,JIT 编译器也会为两个表达式生成相同的汇编代码(因为比较总是由Equals(),null无类型的)。

如果比较涉及用户定义的相等比较器,事情可能会更复杂,在这种情况下,没有规则,它将取决于有效的op_Equals实现。例如这个==实现:

public static bool operator==(MyType lhs, MyType rhs)
{
    if (Object.ReferenceEquals(lhs, rhs))
        return true;

    if (Object.ReferenceEquals(lhs, null))
        return false;

    return lhs.Equals(rhs);
}

在这种情况下,如果第一个操作数是null执行将稍微快一点(因为MyType.Equals()甚至不会被调用),但这种性能增益非常小:您节省了一个比较,很少的跳转和一个虚函数调用。此外,您可以在相反的情况下将函数重写为更快(如果您真的对此很在意)。

这是一个小测试,MyType.Equals(object)只返回true任何非null参数。测试将循环Int32.MaxValue次数:

操作总时间(毫秒)
lhs == null 10521
空== lhs 2346

似乎避免不必要调用的“优化”版本Equals()快了五倍,但请注意循环计数非常高并且实际实现Equals()是空的,真正的实现将减少函数调用的相对开销(可能你会除了这个微优化还有其他事情要做)。对于系统类,您不能依赖此详细信息,例如String.Equals()将始终由操作员调用(无论顺序如何),但您不能假设一个代码路径更快,并且这一事实在框架的未来版本中不会改变(或针对不同的 CPU 架构)。

于 2012-05-30T07:00:07.157 回答
4

不,一般来说这不是真的。比较运算符需要评估它们的两侧,因此将常量放在左侧没有任何好处。但是将它们放在左侧(称为 Yoda 风格)可以减少语言中的编码错误,在这些语言中,您可以在条件中使用赋值运算符,并且您无意中将比较运算符错误地输入==为单个=:

// What you intended to write
if (a == 6) ...
// What you wrote instead
if (a = 6) ... // --> always true as the value of a is changed to 6
// What if Yoda style is used
if (6 = a) ... // --> compile time error
于 2012-05-30T07:04:39.190 回答
2

这是风格问题。有人喜欢写作

if (N == var) ...

只是因为如果你错过了一个=(初学者的常见情况)

if (N = var)

whereN是常量,编译器会报错。

的情况下

if (var = N)

它可能会给您一个警告,也可能不会(取决于编译器和标志)。所以有时很难找到问题所在。

两种变体的性能是相同的。选择你的风格并遵循它。

于 2012-05-30T07:03:38.617 回答
0

Constant [Operator] Actual Value被称为尤达条件,因为它就像“如果它是蓝色的 - 这是天空”或“如果它很高 - 这是一个人”。

这种条件样式的使用在 C/C++ 中很流行,在这种情况下,您可以在键入时为变量赋值,=而不是==. 在 C# 中,使用 Yoda 条件是没有意义的。

我也怀疑它有更好的性能。即使它有一些性能优势,它也会非常小。匹配小于可读性的缺点。

于 2012-05-30T07:05:27.673 回答