使用 == 还是 .Equals() 进行 bool 比较?

16

我在审查一些代码时,发现了以下内容:

public class MyClass
{
    public bool IsEditable { get; set; }

    public void HandleInput()
    {
        if (IsEditable.Equals(false))
        {
            //do stuff
        }
    }
}
据我所知,(IsEditable.Equals(false))(IsEditable == false)是相同的(也与(!IsEditable)相同)。
除了个人喜好之外,在比较布尔值时,.Equals()==有任何区别吗?

2
仅仅是猜测:也许它是从Java移植过来的,因为 ==.Equals 有不同的含义?编辑:或者正如Mike Petty指出的那样,也许它是由一位长期使用Java开发的开发人员编写的,他通常习惯于编写 Equals - Chris Sinclair
可能重复:C#:String.Equals vs. == - Ryan Gates
1
x是一个非空的bool类型表达式时,我从不与truefalse进行比较。使用x而不是x == truex.Equals(true)x != false更容易,并且使用!x而不是x == falsex.Equals(false)x != true更容易。对于两个非常量布尔表达式,我使用C#内置运算符而不是Equals,例如我会使用x == yx != y(有时甚至是x ^ y)。但是对于可空布尔值,例如bool? z,我可能会使用z == false。它具有与(z.HasValue && !z.Value)相同的值。 - Jeppe Stig Nielsen
8个回答

12

实际上,对于像intbool等基本类型,调用Equals()==是有区别的,因为CIL有用于处理这些类型的指令。调用Equals()会强制装箱值并进行虚方法调用,而使用==则会导致使用单个CIL指令。

!valuevalue == false实际上是相同的,至少在搭载.NET 4.0的微软C#编译器中是这样。

因此,在以下方法中进行比较:

public static int CompareWithBoxingAndVirtualMethodCall(bool value)
{
    if (value.Equals(false)) { return 0; } else { return 1; }
}

public static int CompareWithCILInstruction(bool value)
{
    if (value == false) { return 0; } else { return 1; }
    if (!value) { return 0; } else { return 1; } // comparison same as line above
}

将编译为以下CIL指令:

// CompareWithBoxingAndVirtualMethodCall

ldarga.s 'value'
ldc.i4.0
call instance bool [mscorlib]System.Boolean::Equals(bool) // virtual method call
brfalse.s IL_000c // additional boolean comparison, jump for if statement

// CompareWithCILInstruction

ldarg.0
brtrue.s IL_0005 // actual single boolean comparison, jump for if statement

1
这可能会影响性能(但几乎总是可以忽略不计),但永远不会影响表达式的结果。(仅适用于列出的那些类型。) - Servy

12

这主要是一个可读性的问题。我通常会使用 ==,因为我习惯于看它。

特别是对于布尔类型,你根本不需要进行比较。

if(!IsEditable)

就足够了

尽管有时我会像 if (val == false) 这样编写代码,只是为了确保在修改代码时不会读错。


我同意使用val == false而不是!val,因为前者更易于阅读。当你整天盯着代码进行调试时,眼睛很容易忽略掉感叹号。虽然使用前者需要输入更多字符,但编译后的版本是相同的,这才是最重要的。 - Harag

7
< p > Equals方法似乎要慢得多——在调试模式下大约是2.7倍,在发布模式下甚至超过7倍。

这是我的快速而简陋的基准测试:

public static void Main() {
    bool a = bool.Parse("false");
    bool b = bool.Parse("true");
    bool c = bool.Parse("true");
    var sw = new Stopwatch();
    const int Max = 1000000000;
    int count = 0;
    sw.Start();
    // The loop will increment count Max times; let's measure how long it takes
    for (int i = 0; i != Max; i++) {
        count++;
    }
    sw.Stop();
    var baseTime = sw.ElapsedMilliseconds;
    sw.Start();
    count = 0;
    for (int i = 0; i != Max; i++) {
        if (a.Equals(c)) count++;
        if (b.Equals(c)) count++;
    }
    sw.Stop();
    Console.WriteLine(sw.ElapsedMilliseconds - baseTime);
    sw.Reset();
    count = 0;
    sw.Start();
    for (int i = 0; i != Max; i++) {
        if (a==c) count++;
        if (b==c) count++;
    }
    sw.Stop();
    Console.WriteLine(sw.ElapsedMilliseconds - baseTime);
    sw.Reset();
    count = 0;
    sw.Start();
    for (int i = 0; i != Max; i++) {
        if (!a) count++;
        if (!b) count++;
    }
    sw.Stop();
    Console.WriteLine(sw.ElapsedMilliseconds - baseTime);
}

在调试模式下运行,会产生以下结果:

在调试模式下

8959
2950
1874

在发布模式下

5348
751
7

Equals 似乎是最慢的。使用 ==!= 之间似乎没有太大区别。然而,if (!boolExpr) 显然是最佳选择。


4
请查看我的回答(链接为https://dev59.com/h2Yr5IYBdhLWcg3wg6f9#13615362)以了解为什么Equals是最慢的。 - Oliver Hanappi

2

如果你反编译System.Boolean并查看它,那么它的Equals重载定义如下:

public override bool Equals(object obj)
{
  if (!(obj is bool))
    return false;
  else
    return this == (bool) obj;
}

public bool Equals(bool obj)
{
  return this == obj;
}

我希望C#编译器的优化器和.NET JIT编译器足够聪明,至少在发布/优化编译时会将它们内联,使它们完全相同。

1

至少在.NET 4.8中存在差异 - 我认为原因是由Oliver Hanappi的答案中描述的装箱所导致的:

static void Main(string[] args)
{
     object lhs = true;
     object rhs = true;
 
     Console.WriteLine($"Are Equal - {(lhs == rhs ? "Yes" : "No")}"); // Outputs no
     Console.WriteLine($"Are Equal - {(lhs.Equals(rhs) ? "Yes" : "No")}"); // Outputs yes
     Console.ReadLine();
}

-1

==.Equals 始终更好。在整数比较的情况下,== 的运行速度比 .Equals 更快。 在下面的测试中,使用 == 的经过时间为157,而使用 .Equals 的经过时间为230。

class Program
 {        
   static void Main(string[] args)
    {

        Program programObj = new Program();
            programObj.mymethod();
            programObj.mynextmethod();

    }
    void mynextmethod()
    {
        var watch = Stopwatch.StartNew();

        for (int i = 0; i < 60000000; i++)
        {
            int j = 0;
            if (i.Equals(j))

                j = j + 1;
        }
        watch.Stop();
        var elapsedMs = watch.ElapsedMilliseconds;
        Console.WriteLine("Time take in method" + elapsedMs);


        Console.ReadLine();
    }
    void mymethod()
    {
        var watch = Stopwatch.StartNew();

        for (int i = 0; i < 60000000; i++)
        {
            int j = 0;
            if (i == j)

                j = j + 1;
        }
        watch.Stop();
        var elapsedMs = watch.ElapsedMilliseconds;
        Console.WriteLine("Time take in method" + elapsedMs);
    }
}

1
这不是整数比较,而是布尔比较。 - Servy

-1
在这种情况下,使用布尔值没有任何区别,但是对于其他内置的非引用类型来说可能会有所不同。
“==”允许进行类型转换,如果可以的话,“.Equals”则不行。

在一般情况下,operator ==Equals之间有相当多的区别,但没有一个适用于bool。你提出的原因不是那许多原因中的一个;operator ==不会转换任何内容。 - Servy
2
尝试执行 1 == 1.0,它会正常进行比较,但是 1.Equals(1.0) 不会... - Keith Nicholas
operator == 并不会进行任何转换。它实际上就像其他方法一样。它的签名非常特定,因此它的任何重载都无法完全匹配 1 == 1.0,它只能匹配 operator == (double first, double second),编译器将注意到这是最接近的匹配,并且还有从 intdouble 的隐式转换。如果您使用一个带有签名 Equals(double first, double second) 的方法,然后调用 Equals(1, 1.0),它将编译并返回 true。 - Servy
这次再试一下,当解释时不要说它转换任何东西来完成工作。 - Keith Nicholas
operator ==不进行任何转换,这就是我的观点。 C#编译器会在找到适当的重载时进行转换。如果Equals的签名没有涵盖所有情况(即对象和对象),它也会这样做。使用operator ==允许编译器执行隐式转换,而Equals则不允许,但操作符本身仍然不会进行任何转换。这是一个重要的区别。在operator ==的定义中根本没有代码可以转换任何类型。 - Servy
是的...好的,我重新表述一下,以免暗示操作员会这样做。 - Keith Nicholas

-1

请看以下引用来自这里

Equals方法只是在System.Object中定义的虚拟方法,并被选择覆盖。==运算符是一个可以被类重载的运算符,但通常具有标识行为。

对于未重载==的引用类型,它比较两个引用是否指向同一对象-这正是System.Object中Equals的实现方式。

因此,简而言之,Equals最终也只是在执行==操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接