【问题标题】:String Equality operator == in c#C#中的字符串相等运算符==
【发布时间】:2012-02-16 19:24:38
【问题描述】:

我试图查看为 C# 中字符串类中的比较运算符实现的代码。发现是这样的:

//THIS IS NOT WHAT I MEANT
public static bool Equals(object objA, object objB)
{
    return ((objA == objB) || (((objA != null) && (objB != null)) && objA.Equals(objB)));
}

//THIS IS WHAT I SEE REALLY and the above is what I would expect to see
public static bool Equals(string a, string b)
{
    return ((a == b) || (((a != null) && (b != null)) && EqualsHelper(a, b)));
}



public static bool operator ==(string a, string b)
{
    return Equals(a, b);
}

我不知道是不是反射器在欺骗我,但是当我尝试为我自己的类实现这个策略时,我在 Equals 和重载的 == 运算符之间得到了一个无限循环(如预期的那样) .字符串类有什么不同还是我的反射器在报告

static Equals(object o1, object o2)

Object 类的方法成为 String 类的一部分?

【问题讨论】:

  • Reflector 对 objA.Equals 方法有何看法?它不能与静态 Equals 方法相同,因为它是一个实例方法。根据您提供的代码,我没有看到无限循环。你能告诉我们实例 Equals 方法吗?
  • 对不起,我更新了这个问题,我发布了错误的定义...
  • 我认为的无限是在 Equals(string a, string b) 中调用 (a==b) 时发生的。 a 和 b 的 STATIC 类型是字符串,因此调用了重载运算符 ==。我在我的简单示例中对其进行了检查,现在我不确定这里是否存在我看不到的东西,或者它是否是反射器。跨度>
  • 您可以通过完全不使用== 运算符来避免所有这些混淆。使用ReferenceEquals 来检查引用是否相等;它远不那么模棱两可。

标签: c# string operator-overloading equality


【解决方案1】:

C# 中的等式运算符不是多态的。当您评估objA == objB 时,您实际上是在执行==(object a, object b) 运算符实现(检查引用相等),而不是==(string a, string b),因为objA 和objB 变量的声明类型是object,不是string。

您在代码中可能犯的错误是,在评估 == 运算符之前,您没有将类实例转换为 object。

假设你有:

public static bool Equals(MyClass objA, MyClass objB)
{
    return objA == objB || objA != null && objB != null && objA.Equals(objB);
}

...您需要将其替换为:

public static bool Equals(MyClass objA, MyClass objB)
{
    return (object)objA == (object)objB || objA != null && objB != null && objA.Equals(objB);
}

…相当于:

public static bool Equals(MyClass objA, MyClass objB)
{
    return object.ReferenceEquals(objA, objB) || objA != null && objB != null && objA.Equals(objB);
}

更新:String 类包含两个 static bool Equals(string a, string b) 方法和 static bool Equals(object a, object b) 方法。不同之处在于前者是在String 类本身中定义的,而后者是从Object 类(String 的基类)继承而来的。您的反射器可能会也可能不会根据其设置显示继承的方法。

在您发布的代码中,由于objA 和objB 的声明类型为object,因此无论实例的实际类型如何,都会调用具有object 参数的运算符。

更新2:您更新的代码确实包含无限递归。我认为这可能是反射器工具中的一个错误。

更新3:这似乎是反汇编中的一个错误。 Equals(string a, string b) 运算符实现中的第一个条件在反汇编的 C# 代码中显示为 a == b。然而,IL代码的前几行实际上是:

ldarg.0
ldarg.1
bne.un.s IL_0006

ldc.i4.1
ret

bne.un.s 定义为“如果两个无符号整数值不相等(无符号值),则分支到指定偏移处的目标指令,缩写形式。”

因此,看来毕竟是在执行引用相等。

【讨论】:

  • 我现在真的很困惑。假设执行以下代码:
  • 字符串 a = "aaaa";字符串 b = "bbbb";
  • a == b;它调用重载运算符,然后调用 Equals(string a, string b) 方法,然后调用重载运算符...对不起多个 cmets
  • 好的,大概目的是Equals((object)a,(object)b)...谢谢更新
【解决方案2】:

没有String.Equals(object, object) 方法。
你看到的是Object.Equals。

它不递归的原因是objA == objB 调用了内置的对象相等运算符,而不是自定义字符串相等运算符。
(运算符重载是根据操作数的编译时类型来解决的)

【讨论】:

  • 在某种程度上,有一个String.Equals(object, object)方法;它只是没有在String 类中定义,而是从Object 类继承。
  • 是的,但是我的有字符串的参数类型,这会再次调用重载的 == 运算符,而这个会再次调用 Equals 方法等等......
  • @Douglas 静态方法不会被继承。你不能打电话给String.Equals(object, object)。静态的Object.Equals(object, object)方法是in scope在string类中,所以可以不带Object.部分调用,但不被它继承。
  • @phoog:恐怕你弄错了。 bool b = String.Equals(new object(), new object()); 编译(并计算为 false)。
  • @Douglas 你是对的。我一定是误解或记错了我在本周早些时候就此进行的一次谈话。
【解决方案3】:

一个不那么令人困惑的解决方案:不要使用== 运算符:

public static bool Equals(MyClass a, MyClass b) 
{ 
    return ReferenceEquals(a, b)
        || ((!ReferenceEquals(a, null) && !ReferenceEquals(b, null)) && a.Equals(b))); 
} 

【讨论】:

    【解决方案4】:

    它所指的equals方法是这样的:

    public static bool Equals(string a, string b)
    {
        /* == is the object equals- not the string equals */
        return a == b || (a != null && b != null && string.EqualsHelper(a, b));
    }
    
    public static bool operator ==(string a, string b)
    {
        return string.Equals(a, b);
    }
    

    即一个 equals 方法,它接受两个字符串而不是两个对象。

    【讨论】:

      猜你喜欢
      • 2011-04-25
      • 2014-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多