为什么在C#中经常看到“ null!=变量”而不是“ variable!= null”?


103

在c#中,陈述条件的顺序的执行速度是否有差异?

if (null != variable) ...
if (variable != null) ...

从最近开始,我经常看到第一个,自从我习惯了第二个以来,它引起了我的注意。

如果没有区别,第一个的优势是什么?



一些编程语言支持在if条件中初始化值。在这种语言中,如果我们想将LValue与RValue进行比较,则最好在左侧使用文字,例如if(1 == i)。因此,如果不小心将=改为==,则会出现编译器错误。
Jugal Panchal

Answers:


160

这是C语言的保留。在C语言中,如果您使用的是错误的编译器或没有将警告调得足够高,那么它将在没有警告的情况下进行编译(实际上是合法代码):

// Probably wrong
if (x = 5)

当你实际上可能意味着

if (x == 5)

您可以通过以下方法在C中解决此问题:

if (5 == x)

此处输入错误将导致代码无效。

现在,在C#中,这真是个难题。除非您要比较两个布尔值(IME很少见),否则您可以编写更具可读性的代码,因为“ if”语句要求以布尔值开头,而“ x=5” 的类型为Int32,不是Boolean

我建议,如果您在同事的代码中看到了这一点,请以现代语言的方式对其进行教育,并建议他们将来编写更自然的形式。


6
我的同事们对此一无所知,甚至在if ...之后也只想声明一个大括号,以防万一(如果您以后添加一些内容而没有意识到)。我说,如果您的语言在不缩进的情况下不可读,请继续使用F#和Boo(和YAML),使其成为语言的一部分。
杰尔

48
添加括号合理的,IMO-C#肯定不会尝试阻止它成为问题。这与“常量==变量”问题非常不同。
乔恩·斯基特

6
一个if(this!= that){performAsSuch(); }实际上非常健康。“以后加法”的论点在我看来像在避免或发现(a = 5)错别字一样脆弱
jpinto3912

3
@jpinto:我真的不知道你想什么,在这里说-你喜欢围绕单一语句大括号,或者说你没有?此问题与变量业务之间的区别在于,在C#中带有错字的代码是无效代码,因此编译器将停止它。不穿括号也不一样
。–乔恩·斯基特

3
@Benjol不要忘记总是提供一个空else { }子句,以防以后有人需要在其中添加一些内容而忘记自己编写else关键字。(笑话)
杰普·斯蒂格·尼尔森

12

有充分的理由先使用null: if(null == myDuck)

如果您class Duck覆盖==运算符,则if(myDuck == null)可能会陷入无限循环。

使用nullfirst将使用默认的相等比较器,并实际执行您想要的操作。

(我听说您最终习惯了以这种方式编写的代码的阅读-我只是还没有经历过这种转换)。

这是一个例子:

public class myDuck
{
    public int quacks;
    static override bool operator ==(myDuck a, myDuck b)
    {
        // these will overflow the stack - because the a==null reenters this function from the top again
        if (a == null && b == null)
            return true;
        if (a == null || b == null)
            return false;

        // these wont loop
        if (null == a && null == b)
            return true;
        if (null == a || null == b)
            return false;
        return a.quacks == b.quacks; // this goes to the integer comparison
    }
}

11
错了 不赞成投票。只需将鼠标悬停在==运算符上并将其停在那儿,您将看到在两种情况下都使用了用户定义的重载。当然,它可以使一个微妙的差异,如果你检查a之前b,然后交换的位置a从右操作数左操作数。但是您也可以b在之前检查a,然后b == null将其反转顺序。让操作员自己打电话,这一切都令人困惑。而是,强制转换任一操作数(或同时转换两者)object以获得所需的重载。喜欢if ((object)a == null)if (a == (object)null)
杰普·斯蒂格·尼尔森

10

就像每个人都已经指出的那样,它或多或少地来自C语言,如果您不小心忘记了第二个等号,则可能会得到错误的代码。但是还有另一个与C#相匹配的原因:可读性。

仅举一个简单的例子:

if(someVariableThatShouldBeChecked != null
   && anotherOne != null
   && justAnotherCheckThatIsNeededForTestingNullity != null
   && allTheseChecksAreReallyBoring != null
   && thereSeemsToBeADesignFlawIfSoManyChecksAreNeeded != null)
{
    // ToDo: Everything is checked, do something...
}

如果您只是将所有词交换到开头,则可以更轻松地发现所有检查:

if(null != someVariableThatShouldBeChecked
   && null != anotherOne
   && null != justAnotherCheckThatIsNeededForTestingNullity
   && null != allTheseChecksAreReallyBoring
   && null != thereSeemsToBeADesignFlawIfSoManyChecksAreNeeded)
{
    // ToDo: Everything is checked, do something...
}

因此,该示例可能是一个不好的示例(请参阅编码指南),但请考虑一下您快速滚动浏览完整的代码文件。通过简单地看到模式

if(null ...

您立即知道接下来会发生什么。

如果相反,您总是必须扫描到该行的末尾以查看无效检查,只是让您绊倒一秒钟以找出进行哪种检查。因此,语法突出显示也许可以为您提供帮助,但是当这些关键字位于行尾而不是行尾时,您总是会变慢。


9

我猜这是一个切换语言的C程序员。

在C中,您可以编写以下内容:

int i = 0;
if (i = 1)
{
    ...
}

注意那里使用了一个等号,这意味着代码将为变量i分配1,然后返回1(一个赋值是一个表达式),并在if语句中使用1,这将被视为true。换句话说,以上是一个错误。

但是,在C#中,这是不可能的。两者之间确实没有区别。


1
在没有编译器抱怨的情况下,“但是在C#中是不可能的”,因为if语句需要一个布尔表达式,并且所提供的是int类型。
罗伯特·哈维

4

在早期,人们会忘记“!” (或额外的'='表示相等性,这很难发现),然后分配而不是进行比较。将null放在前面可以消除该错误的可能性,因为null不是l值(即不能分配给IE)。

在当今有条件的情况下执行赋值操作时,大多数现代编译器都会发出警告,而C#实际上会给出错误。大多数人只是坚持使用var == null方案,因为它对某些人来说更易于阅读。


所有C#编译器(请注意,这是一个C#问题)均应无法编译表达式不是布尔值的“ if”语句……
Jon Skeet

4

遵循此约定我看不出任何优势。在不存在布尔类型的C语言中,编写

if( 5 == variable)

而不是

if (variable == 5)

因为如果忘记其中一个等号,您最终会得到

if (variable = 5)

将5赋给变量,并始终求值为true。但是在Java中,布尔值是布尔值。并且使用!=根本没有任何理由。

不过,一个好的建议是写

if (CONSTANT.equals(myString))

而不是

if (myString.equals(CONSTANT))

因为它有助于避免NullPointerExceptions。

我的建议是要求对规则进行辩护。如果没有,为什么要遵循?它不利于可读性


0

对我来说,一直是您喜欢哪种风格

@Shy-再一次,如果您使操作员感到困惑,那么您应该会遇到编译错误,否则您将在运行代码时遇到一个错误-一个错误会再次出现并在以后咬住您,因为它产生了意外的行为


我不认为我曾经听到有人宁愿“恒==变量”形式的其他较安全的原因,这是已经在C#中处理。
乔恩·斯基特

我本人更喜欢“变量==常量”,但是我已经看到程序员采用了相反的方法,因为他们觉得它对他们来说更自然。
TheCodeJunkie

1
难道他们都是经过多年洗脑的人,还是一个真正的选择?
乔恩·斯基特

0

正如许多人指出的那样,它主要是在旧的C代码中用于识别编译错误,因为编译器认为它是合法的

像Java这样的新编程语言go足够聪明,可以捕获此类编译错误

人们不应该像代码中的条件那样使用“ null!= variable”,因为它非常不可读


-4

还有一件事...如果将变量与常量(例如整数或字符串)进行比较,则将常量放在左侧是一种好习惯,因为您永远不会遇到NullPointerExceptions:

int i;
if(i==1){        // Exception raised: i is not initialized. (C/C++)
  doThis();
}

int i;
if(1==i){        // OK, but the condition is not met.
  doThis();
}

现在,由于默认情况下C#会实例化所有变量,因此您不应该在该语言中遇到该问题。


1
我不确定第一部分代码使用的是哪个编译器,但是应该编译(通常带有警告)。i的值是不确定的,但确实有一些值。
海豚2009年

当然,两个代码都可以编译!这就是问题所在。尝试运行第一个代码,您将理解“异常引发”的含义...
karlipoppins

两者之间也没有区别。你可以ellaborate吗?
mfazekas

除非您在C / C ++中声明int * i,否则不会有异常,即使在这种情况下,它也将比较指针并且不会引发任何异常……就像Dolphin声明的,该值是未定义的,但不会生成任何异常。
Cobusve

没有例外,可以说i是未经适当初始化的随机值。该表达式在c / c ++中具有完全相同的语义
Raffaello
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.