C#中的字符串相等运算符“==”

4

我试图查看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方法吗? - ken
抱歉,我在这个问题上进行了更新,我发布了一个错误的定义... - Bober02
1
在我的观点中,当在Equals(string a, string b)中调用(a==b)时,无限发生。a和b的STATIC类型都是字符串,因此会调用重载运算符==。我在我的简单示例中进行了检查,现在我不确定是否有我没有看到的东西或者是Reflector在撒谎。 - Bober02
你可以通过完全不使用 == 运算符来避免所有这些混淆。使用 ReferenceEquals 来检查引用相等性,它要少得多含糊不清。 - phoog
4个回答

8
在C#中,等号操作符不具有多态性。当您评估objA == objB时,实际上执行的是==(object a, object b)操作符实现(检查引用相等性),而不是==(string a, string b),因为objAobjB变量的声明类型是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类包含两个静态方法bool Equals(string a, string b)bool Equals(object a, object b)。区别在于前者是在String类内部定义的,而后者是从Object类继承而来的(StringObject类的基类)。您的反射器可能会根据其设置显示或不显示继承的方法。
在您发布的代码中,由于objAobjB的声明类型为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 被定义为“如果两个无符号整数值不相等(无符号值),则跳转到指定偏移处的目标指令(短格式)。”

因此,看起来实际上进行了引用相等性比较。


我现在非常困惑。据说以下代码会执行: - Bober02
字符串 a = "aaaa"; 字符串 b = "bbbb"; - Bober02
a == b;它调用了重载运算符,然后调用Equals(string a, string b)方法,该方法又调用了重载运算符... - Bober02
好的,大概意思是Equals((object)a, (object)b)…感谢更新。 - Bober02

7

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

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


从某种意义上说,确实有一个String.Equals(object, object)方法;它只是没有在String类中定义,而是从Object类继承而来。 - Douglas
是的,但我的参数类型是字符串,这将导致对重载的 == 运算符进行另一个调用,而这个调用将再次调用 Equals 方法,以此类推... - Bober02
@Douglas 静态方法不会被继承。您无法调用 String.Equals(object, object)。静态的 Object.Equals(object, object) 方法在字符串类中是在范围内的,因此可以在没有 Object. 部分的情况下调用它,但它并未被继承。 - phoog
@Douglas 这次谈话我现在想起来是关于通用类型参数的。即使 T 的类型约束定义了该方法,你也不能在通用类型参数上调用静态方法。 - phoog
@phoog:没错。但是考虑到静态方法不具有多态性,如果允许在泛型类型上调用静态方法,那么这种调用总是等价于在类型约束上调用静态方法,对吧? - Douglas
显示剩余5条评论

1
一个不那么令人困惑的解决方案是:不要使用==运算符。
public static bool Equals(MyClass a, MyClass b) 
{ 
    return ReferenceEquals(a, b)
        || ((!ReferenceEquals(a, null) && !ReferenceEquals(b, null)) && a.Equals(b))); 
} 

0
它所指的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);
}

即一个接受两个字符串而不是两个对象的等于方法。


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