【问题标题】:Using == or .Equals() for bool comparison使用 == 或 .Equals() 进行布尔比较
【发布时间】:2012-11-28 21:34:52
【问题描述】:

我正在查看一些代码,发现如下所示:

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

    public void HandleInput()
    {
        if (IsEditable.Equals(false))
        {
            //do stuff
        }
    }
}

据我所知,(IsEditable.Equals(false)) 与(IsEditable == false) 相同(也与(!IsEditable) 相同)。

除了个人喜好,.Equals() 和== 之间有什么区别,特别是用来比较bools?

【问题讨论】:

  • 只是猜测:可能是从 Java 移植过来的,== 和 .Equals 有不同的含义?编辑:或者正如 Mike Petty 所指出的,也许它是由一位习惯于正常编写 Equals 的长期 Java 开发人员编写的。
  • 可能重复:C#: String.Equals vs. ==
  • 当x 是bool 类型(不可为空)的表达式时,我从不与true 或false 比较。仅使用x 而不是x == true 或x.Equals(true) 或x != false 更容易,更容易使用!x 而不是x == false 或x.Equals(false) 或x != true。等等。对于 两个 非常量布尔表达式,我使用 C# 内置运算符而不是 Equals,例如我会使用 x == y 或 x != y(有时甚至是 x ^ y)。但是对于 nullable 布尔值,例如bool? z,我可能会使用z == false。它与(z.HasValue && !z.Value) 具有相同的值。

标签: c#


【解决方案1】:

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

特别是布尔值,你根本不需要比较它们

if(!IsEditable)

足够了

虽然,有时我自己会写if (val == false) 之类的东西,只是为了确保在我必须修改代码时不会误读它。

【讨论】:

  • 我同意 val == false 而不是 !val 因为它更容易阅读,而且当您整天都在看代码并且正在寻找错误时,您的眼睛很容易错过!象征。是的,输入 blah blah 的字符更多,但编译后的版本是一样的,这很重要。
【解决方案2】:

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

!value 和value == 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

【讨论】:

  • 这可能会影响性能(几乎总是可以忽略不计),但不会影响表达式的结果。 (仅适用于列出的那些类型。)
【解决方案3】:

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】:

如果你反编译 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 编译器足够聪明,可以内联这些,至少对于发布/优化编译而言,使它们完全相同。

【讨论】:

    【解决方案5】:

    有一个区别 - 至少在 .NET 4.8 中 - 我相信原因是因为Oliver Hanappi's 回答中描述的拳击:

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

    【讨论】:

      【解决方案6】:

      看看下面的引用Taken from here:

      Equals 方法只是在 System.Object 中定义的一个虚拟方法,并且 被选择这样做的任何类覆盖。 == 运算符是 可以由类重载的运算符,但通常具有 身份行为。

      对于 == 没有被重载的引用类型,它比较 两个引用是否引用同一个对象 - 这正是 Equals 的实现在 System.Object 中做了什么。

      简而言之,Equals 实际上只是在做一个 == 反正。

      【讨论】:

        【解决方案7】:

        在这种情况下,使用 bool 没有任何区别,但是使用其他内置的非引用类型则可以。

        == 允许类型转换,如果它可以.Equals 不会

        【讨论】:

        • 在一般情况下operator == 和Equals 之间存在相当多的差异,这些差异都不适用于bool。您给出的一个原因不是众多原因之一。 operator == 不转换任何东西。
        • 尝试做 1 == 1.0 它会很好地进行比较,但 1.Equals(1.0) 不会......
        • operator == 没有转换任何内容。它实际上只是一种方法,就像其他任何方法一样。它的签名非常具体,以至于它的重载不会完全匹配1 == 1.0,它只会匹配operator == (double first, double second),编译器会注意到这是最接近的匹配,并且还有从int到的隐式转换double。如果您使用带有签名Equals(double first, double second) 的方法,然后调用Equals(1, 1.0),它将编译并返回true。
        • 再试一次,这一次解释它没有转换任何东西,不要说它转换了东西来完成它的工作。
        • operator == 不会转换任何东西,这就是我的观点。 C# 编译器正在执行转换,因为找到了适当的重载。如果Equals 的签名还没有涵盖everything(作为对象和对象),它也会这样做。使用operator == 允许编译器执行隐式转换,而Equals 不允许,但运算符本身仍然不进行任何转换。这是一个重要的区别。在operator == 的定义中没有任何代码可以转换任何类型。
        【解决方案8】:

        == 总是比.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);
            }
        }
        

        【讨论】:

        • 这不是整数比较。这是一个布尔比较。
        猜你喜欢
        • 1970-01-01
        • 2020-06-04
        • 1970-01-01
        • 2016-06-22
        • 2021-12-06
        • 1970-01-01
        • 1970-01-01
        • 2014-08-07
        • 2019-05-28
        相关资源
        最近更新 更多