如果"==运算符未定义"会发生什么?

3

"==运算符未定义"会发生什么?

例如:

class a
{
    int variable = 0;
}
class b
{
    void proc()
    {
        a ref1 = new a();
        a ref2 = new a();
        bool cmp1 = ref1 == ref2;//?
        bool cmp2 = ref1 == ref1;//?
    }
}

在使用结构体时有何不同?

当使用序列化对象(单例)(System.Runtime.Remoting.*)时呢?


没有人回答最后一部分吗?(“关于序列化(System.Runtime.Remoting.*)对象(单例)怎么样?”) - Behrooz
这些对象继承自哪种类型?如果是MarshalByRefObject,那么它们将是引用(因为MarshalByRefObject是一个抽象类),因此默认情况下,只有两个变量指向同一实例时,==才会返回true。 - Justin Ethier
2
你的问题在规范的第7.9节中得到了回答。请查看详细信息。 - Eric Lippert
FYI:http://msdn.microsoft.com/en-us/library/aa664727(VS.71).aspx - Andras Vass
4个回答

4
对于用户定义的值类型,您的代码将无法编译。具体而言,它会出现以下错误:"Operator '==' cannot be applied to operands of type 'a' and 'a' ". "除非结构体显式重载了这些运算符,否则 == 和 != 运算符不能用于结构体。" 您必须重载两个运算符。您很可能不想在方法中使用默认的Equals(),因为 "...对于结构体,默认实现的 Object.Equals(Object)(是 System.ValueType 中的重写版本)通过使用反射来比较类型中每个字段的值来执行值相等性检查。当实现者在结构中重写虚拟 Equals 方法时,目的是提供一种更有效的执行值相等性检查的方法,并有选择地基于结构的某些字段或属性进行比较。" 对于用户定义的引用类型(简化情况,如 OP 示例): "即使类没有重载它们,== 和 != 运算符也可以与类一起使用。但是,默认行为是执行引用相等性检查。在类中,如果重载了 Equals 方法,则应重载 == 和 != 运算符,但这不是必需的。" 如果您没有重载运算符,那么很可能只会进行引用相等性测试。
“简化情况”,因为运算符重载解析可能会选择另一个实现而不是默认实现
//Minimal example, for demonstration only.
//No Equals(), GetHaschode() overload, no IEquatable<T>, null checks, etc..
class Program
{
    static void Main()
    {

        MyMoreDerived a = new MyMoreDerived() { fbase = 1, fderived = 3 };
        MyMoreDerived b = new MyMoreDerived() { fbase = 2, fderived = 3 };

        //Even though MyMoreDerived does not overload the operators, this
        //will succeed - the definition in MyDerived will be used.
        if (a == b)
        {
            //Reached, because the operator in MyDerived is used.
            Console.WriteLine("MyDerived operator used: a == b");
        }

        a.fderived = 2;
        b.fbase = 1;
        //a => {1, 2} 
        //b => {1, 3}
        //Since 2 != 3, the operator in MyDerived would return false.
        //However only the operator in MyBase will be used.
        if ((MyBase)a == (MyBase)b)
        {
            //Reached, because the operator in MyBase is used.
            Console.WriteLine("MyBase operator used: a == b");
        }

        b.fderived = 2;
        //a => {1, 2} 
        //b => {1, 2}
        //Now both operator definitions would compare equal,
        //however they are not used.
        if ((object)a != (object)b)
        {
            //Reached, because the default implementation is used
            //and the references are not equal.
            Console.WriteLine("Default operator used: a != b");
        }

    }

    class MyBase
    {
        public int fbase;

        public static bool operator ==(MyBase x, MyBase y)
        {
            return x.fbase == y.fbase;
        }

        public static bool operator !=(MyBase x, MyBase y)
        {
            return x.fbase != y.fbase;
        }

    }

    class MyDerived : MyBase
    {
        public int fderived;

        public static bool operator ==(MyDerived x, MyDerived y)
        {
            return x.fderived == y.fderived;
        }

        public static bool operator !=(MyDerived x, MyDerived y)
        {
            return x.fderived != y.fderived;
        }

    }

    class MyMoreDerived : MyDerived
    {
    }

}

单例在引用类型的上下文中最有意义,它们的目的是返回一个特定的实例。我无法想象出一个合理的情况,其中引用相同但对象本身不“等于”自己。

即使使用远程调用,将操作契约与数据契约分开仍然是最佳实践。 前者通常由服务器端的MarshalByRefObject实现 - 实现由接口定义的操作,而后者则使用按值编组的数据/消息类,并可能由客户端和服务器共享。如果您在数据类中重载运算符,这可能并不是一个大问题。但是,这些类不应引用/调用远程对象,我认为。

即使您提供了自定义客户端代理来重载运算符,我认为在==!=运算符背后隐藏远程调用是一种非常糟糕的做法,也会导致调试困难。 (如果我理解您的意图,我不确定。)


1

当 == 没有被重写时,我相信它会比较引用,检查它们是否是同一个对象。

例如:

MyClass a = new MyClass(1);
MyClass b = new MyClass(1);
MyClass c = a;

if (a == b) // false
    ...
if (a == c) // true
    ...

因此,在您上面的代码中,cmp1将为false,而cmp2将为true。
但是,对于用户定义的值类型,它比较类型的实际值。

不,它不会比较实际值。==!=未针对用户定义的值类型进行定义。你可能会被误导认为这些运算符将像Equals()一样工作,使用反射对用户定义的结构进行逐字段比较。然而,事实并非如此。 - Andras Vass

1

它可能会比较指针'a'和'b',看它们是否指向内存中的同一对象。

如果您需要比较这些对象的字段,则必须定义比较器函数。

您需要从IComparable接口继承并定义CompareTo方法。

请参阅此处:IComparable接口


序列化的对象会发生什么? - Behrooz
不,你不必为相等性检查实现IComparable接口。你应该按照这里的步骤进行操作:http://msdn.microsoft.com/en-us/library/dd183755.aspx 如果你想定义元素的完全顺序,即想对它们进行排序/排序,那么你需要实现IComparer<T>IComparable<T>接口。 - Andras Vass

1

来自 MSDN:

对于预定义的值类型,等于运算符(==)将返回 true,如果其操作数的值相等,则返回 true,否则返回 false。对于除字符串之外的引用类型,如果它的两个操作数引用同一个对象,则 == 将返回 true。 对于字符串类型,== 比较字符串的值。


1
用户定义的值类型呢? - Aistina
3
对于用户定义的值类型,默认实现的==将不返回任何内容,因为对于用户定义的值类型,“==”和“!=”运算符没有默认实现。它无法编译。 - Andras Vass

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