【问题标题】:C# if statement. inner workings QC# if 语句。内部工作Q
【发布时间】:2010-05-27 14:19:13
【问题描述】:

我刚刚在一些 Exchange 2010 代码中遇到了这个代码 sn-p,我想知道是否有人知道为什么程序员会这样做。我从未见过这样格式的 If 语句。这么倒退,一定是有原因的吧??

if (true == MsgItem.HasAttachments)
{
    // Code
}

我假设它可能对同一事物的各种其他编码方式进行了一些优化;

if (MsgItem.HasAttachments) 
{
    // Code
}

if (MsgItem.HasAttachments == true)
{
    // Code
}

没什么大不了的,我只是好奇。

谢谢, 迈克

更新:感谢提出的所有有趣的观点。总结似乎取决于遗留的编码标准。

【问题讨论】:

标签: c#


【解决方案1】:

这是 C 或 C++ 遗留下来的习惯,您可能会不小心这样做:

if (i = 1) {
}

这意外地将1 分配给i,而不是进行比较。

颠倒支票中的顺序可以防止这种常见错误。

if (1 = i) {
}

那不会编译,很容易看出你错误地输入了=而不是==

这仍然与 C# 中的 bools 相关,因为赋值的值就是被赋值的值。

为此:

if (SomeBoolVar = true) {
}

将始终为真,因为您将true 存储到SomeBoolVar 中,其计算结果为true。一个很难追查的小错误。

【讨论】:

  • 使用if (SomeBoolVar = true)而不是if (SomeBoolVar)的人应该被砍掉双手:-)
  • 哈哈,至少他们的大拇指,这样的空格键使用起来很蛋疼。 (@paxdia)
  • 如果您在条件表达式中不小心使用了= 而不是==,Microsoft 的 C# 编译器也会抛出警告。
  • @LukeH,Jon Skeet 怎么回答这个问题,他说它会在没有警告的情况下编译?我不经常使用 C#,所以我不了解自己,但我会惊讶地发现 C# 编译器并没有对此发出警告……自黑暗时代以来,每个 C++ 编译器都会对此发出警告。
  • @rmeador:我不知道 Jon 为什么这么说——也许你应该直接问他——但是 C# 编译器(使用默认设置)肯定会显示该警告。 msdn.microsoft.com/en-us/library/c1sde1ax.aspx
【解决方案2】:

C++ 和 C 程序员有时会将常量放在首位,以免误写

if (variable = constant)

执行任务。

特别是明确地用于布尔比较,假设HasAttachments 是可写的,该语句将编译并运行没有警告1,但在 C# 中没有预期的行为:

if (MsgItem.HasAttachments = true)

对于非布尔表达式,这在 C# 中通常不是问题,因为 if 语句的条件必须可以隐式转换为 bool... 对于布尔表达式,无论如何,中间形式是首选:

if (MsgItem.HasAttachments)

如果它对性能有任何影响,我会感到惊讶 - 并且可能对生成的 IL 开始时没有影响。


1 糟糕——MS C# 编译器确实对此发出警告:

警告 CS0665:条件表达式中的赋值始终是常量;你的意思是用 == 代替 = 吗?

我还没有在 Mono 下尝试过;这可能会或可能不会发出警告。这肯定是有效的 C# 代码。

【讨论】:

  • 信不信由你,它确实转化为稍微不同的IL。请参阅 jeffmaphone 对a very similar question I posted a long time ago 的回答。
  • @Dan:jeffmaphone 的答案是使用 C++,而不是 C#。在 C# 中,它们将编译为完全相同的 IL。
  • @LukeH:哈,谢谢你提醒我(在链接之前应该重新阅读答案——忽略了这个问题是 C# 特定的,而我的问题与语言无关)。而且我注意到您是在上述问题中指出这一事实的人!
  • 你为什么说它会在没有警告的情况下编译?有理由认为,编译器会足够聪明地对此发出警告,LukeH 在对贾斯汀·尼尔森的评论中说了同样的话 - jjnguy 的回答,已被赞成......
  • @rmeador:我认为它不会发出警告,但幸运的是编译器确实对此发出警告。将编辑。
【解决方案3】:

这些被称为“Yoda Conditions”——最初的想法是如果你不小心输入了一个=,编译会失败,因为常量值在左边。

【讨论】:

    【解决方案4】:

    因为程序员是个小丑 :-) 布尔条件的全部意义在于您将它们命名为使它们有意义为布尔值:

    if (MsgItem.HasAttachments)
    

    这显然是这样,所以我不知道为什么编码员会通过测试与true 的相等性来打自己的脚。

    因为,说真的,true == MsgItem.HasAttachments 只是另一个布尔值,所以你在哪里停下来?

    if (true == MsgItem.HasAttachments)
    if (true == (true == MsgItem.HasAttachments))
    if (true == (true == (true == MsgItem.HasAttachments)))
    : : :
    

    以此类推,无穷无尽。

    首先使用常量以避免意外使用= 赋值而不是== 相等的问题在这里不应该成为问题,因为您不应该检查布尔值与truefalse。您应该使用以下之一:

    if (cond)
    if (!cond)
    

    【讨论】:

      【解决方案5】:

      if (true == myvar) 是有充分理由的:变量(或表达式)可能不是是布尔表达式——它可能是bool?。那么表达式将等同于if(myvar ?? false) - 我更喜欢第二种语法,因为很明显您正在处理一个可为空的值。从技术上讲,可能正在使用自定义 == 运算符(可能与隐式类型转换结合使用),但这不太可能,因为这样做通常不受欢迎,也不实用或不方便。

      正如许多其他人指出的那样,将常量放在首位可能会减少意外错误 - 就我个人而言,我认为这些错误的发生频率并不高,但这不会造成伤害。

      然而,原作者很可能在编写该代码的那一刻有点困惑。

      【讨论】:

        【解决方案6】:

        在单声道下,它们都生成相同的 IL (ldloc.0; brfalse <LOC>):

        来源:

        using System;
        class Foo
        {
            static void Main()
            {
                bool bar = Console.ReadLine() == "bar";
                if (bar)
                {
                    Console.WriteLine("bar");
                }
        
                if (true == bar)
                {
                    Console.WriteLine("true == bar");
                }
        
                if (bar == true)
                {
                    Console.WriteLine("bar == true");
                }
            }
        }
        

        IL:

        // snip...
        IL_000a:  call bool string::op_Equality(string, string)
        IL_000f:  stloc.0 
        IL_0010:  ldloc.0 
        IL_0011:  brfalse IL_0020
        
        IL_0016:  ldstr "bar"
        IL_001b:  call void class [mscorlib]System.Console::WriteLine(string)
        IL_0020:  ldloc.0 
        IL_0021:  brfalse IL_0030
        
        IL_0026:  ldstr "true == bar"
        IL_002b:  call void class [mscorlib]System.Console::WriteLine(string)
        IL_0030:  ldloc.0 
        IL_0031:  brfalse IL_0040
        // snip...
        

        就个人而言,当人们这样做 if (SomePredicate() == true) 时,我会发疯。除非它实际上返回一个带有重载 == 运算符的对象,否则与 true 的比较是完全没有必要的。为什么只进行一次比较?

        // make extra sure SomePredicate returns true
        if ((SomePredicate() == true) == true) 
        {
             // ...
        } 
        

        【讨论】:

          【解决方案7】:

          也许== 运算符已重载,MsgItem.HasAttachments 有更多信息,而只是bool?我不知道。只是一个想法。

          【讨论】:

          • 啊,旧的“每日 WTF”{true,false,maybe} 类型 :-)
          • 不,如果你重载相等运算符,事情并不总是它们看起来的那样。如果您有 'true == MsgItem.HasAttachments',它将为 bool 类运行 == 运算符并将 MsgItem.HasAttachments 传递给运算符实现。如果反过来,它将对 HasAttachments 的类型运行 == 运算符实现并将“true”传递给它。显然,行为不必相同。因此,至少在重载运算符时,顺序通常会产生影响。
          【解决方案8】:

          这是编写关键任务代码时的常见要求。当您打算进行检查时,它可以防止意外分配。在像 C# 这样的语言中,编译器会警告你,所以它不是必需的,但它是许多程序员遗留下来的习惯。

          【讨论】:

            猜你喜欢
            • 2016-03-26
            • 2016-01-28
            • 1970-01-01
            • 2019-07-23
            • 1970-01-01
            • 1970-01-01
            • 2014-12-02
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多