“Equals”和“==”有什么不同吗?
两个String怎样看是否相等?
string,值类型和引用类型。关于"相等性"我们了解了吗?==是什么?!为什么值类型不能使用==,只能使用 重载方法的 operator ==
为什么值类型可以使用ReferenceEqualsusing System;
using System.Text;class Test
{
    
    static bool ReferenceCompare<U, V>(U obj1, V obj2)
    {
        return object.ReferenceEquals(obj1, obj2);
    }    static bool ClassEqualsCompare<U, V>(U obj1, V obj2)
        where U:class
        where V:class
    {
        return obj1 == obj2;
    }    //运算符“==”无法应用于“U”和“V”类型的操作数
    static bool StructEqualsCompare<U, V>(U obj1, V obj2)
        where U : struct
        where V : struct
    {
        return object.ReferenceEquals(obj1, obj2);
        //return obj1 == obj2;
    }    //项目属性对话框->配置属性->生成->允许不安全代码块->设为true
    unsafe static void Main(string[] args)
    {
        //-----------------------------------------
        string s1 = "abc";
        string s2 = "abc";
        string s3 = s1;        Console.WriteLine(ReferenceCompare(s1, s2));
        Console.WriteLine(ReferenceCompare(s1, s3));        Console.WriteLine(ClassEqualsCompare(s1, s2));
        Console.WriteLine(ClassEqualsCompare(s1, s3));        Console.WriteLine("s1'code:{0:X8}", s1.GetHashCode());
        Console.WriteLine("s2'code:{0:X8}", s2.GetHashCode());
        Console.WriteLine("s3'code:{0:X8}", s3.GetHashCode());        //-----------------------------------------
        StringBuilder sb1 = new StringBuilder("abc");
        StringBuilder sb2 = new StringBuilder("abcD");
        StringBuilder sb3 = sb1;        Console.WriteLine(ReferenceCompare(sb1, sb2));
        Console.WriteLine(ReferenceCompare(sb1, sb3));        Console.WriteLine(ClassEqualsCompare(sb1, sb2));
        Console.WriteLine(ClassEqualsCompare(sb1, sb3));        Console.WriteLine("sb1'code:{0:X8}", sb1.GetHashCode());
        Console.WriteLine("sb2'code:{0:X8}", sb2.GetHashCode());
        Console.WriteLine("sb3'code:{0:X8}", sb3.GetHashCode());        //-----------------------------------------
        int i1 = 100;
        int i2 = 100;
        int i3 = i1;        Console.WriteLine(ReferenceCompare(i1, i2));
        Console.WriteLine(ReferenceCompare(i1, i3));        //ValueType是不可继承的,并且没有==
        //值类型派生自ValueType        Console.WriteLine("i1'address:{0:X8}", (uint)&i1);
        Console.WriteLine("i2'address:{0:X8}", (uint)&i2);
        Console.WriteLine("i3'address:{0:X8}", (uint)&i3);        Console.ReadKey();
    }
}还有更多的相关问题,等待大家来发掘.

解决方案 »

  1.   

    看msdn + il
    应该明白的差不多了
      

  2.   

    搞清几个概念,是很容易分清的。
    一、值类型与引用类型的区别1.值类型是存储在内存中的堆栈(栈).
    2.引用类型的变量在栈中仅仅是存储引用类型变量的地址,而其本身则存储在堆中。举个例子:XX招待所305房间,住着张三。306房间,住着李四。
      张三、李四 对应值类型
      305、306 对应引用类型  305、306房客是否是同一个人,是引用的比较  结果:不等  第二天,李四走了,李三全包下来,  305、306房客是否是同一人       结果:相等 (引用)                                                             张三永远不是李四 (值)  
    二、比较相等的方法:1.运算符 (==)
      a.如果对象的值相等,则相等运算符 (==) 返回 true,否则返回 false。
      b.string 以外的引用类型,如果两个对象引用同一个对象,则 == 返回 true。
      c.string 类型,== 比较字符串的值。2.equals()方法  适用:比较的是两个对象的内容是否一致,即:比较引用类型是否是对同一个对象的引用。
      

  3.   

    System.Object 类型提供了一个名为 Equals 的虚方法,其目的为判断两个对象是否有着相同的“”。因为 Equals 方法定义在 Object 中,而每个类型最终都派生自 Object,所以我们可以保证每个类型的实例都有一个这样的 Equals 方法。对于那些没有显式重写 Equals 方法的类型,Object(或者重写了 Equals 方法的最近的那个基类)提供的的实现将被继承。下面的代码展示了 System.Object 类型中的 Equals 方法实现:class Object
    {
      public virtual bool Equals(object o)
      {
        // 如果两个引用指向的是同一个对象,它们肯定相等
        if (this == o) return true;
        
        // 假定两个对象不相等
        return false;
      }
    }如果我们定义了自己的类型,并且希望比较它们中的字段是否相等,Object 类型提供的默认实现对我们来说是不够的,我们必须重写 Equals 方法,提供自己的实现。
      

  4.   

    http://msdn.microsoft.com/zh-cn/library/7h9bszxx.aspx
    .NET Framework 开发人员指南
    Equals 和相等运算符 (==) 的实现准则在值类型中实现相等运算符 (==) 
    大多数编程语言中都没有用于值类型的默认相等运算符(==)实现。在引用类型中实现相等运算符 (==) 
    大多数语言确实为引用类型提供默认的相等运算符(==)实现。msdn的上述论述可以解释LZ的这个问题:引用类型有默认的相等运算符(==)实现,而值类型没有。    static bool ClassEqualsCompare<U, V>(U obj1, V obj2)
            where U:class
            where V:class
        {
            return obj1 == obj2;
        }    //运算符“==”无法应用于“U”和“V”类型的操作数
        static bool StructEqualsCompare<U, V>(U obj1, V obj2)
            where U : struct
            where V : struct
        {
            return object.ReferenceEquals(obj1, obj2);
            //return obj1 == obj2;
        }
      

  5.   


    因为 object.ReferenceEquals 是公有的静态方法,它的两个参数都是 object 类型的,而每个类型最终都派生自 object,所以 object.ReferenceEquals 可用于任何类型,包括LZ说的值类型。
    http://msdn.microsoft.com/zh-cn/library/system.object.referenceequals.aspx
    public static bool object.ReferenceEquals(object objA, object objB)
      

  6.   


    至于这段,我不认同你的观点.每个类型最终都派生自 object
    这句话是有语境的,单指继承性没问题,但在这个环境下不太适合,
    //System.Object->System.ValueType->struct
    我试图继承ValueType类来试验一下,不允行(这个也行奇怪,MSDN对这种情况有说明嘛?)实际上对于很多特殊性型是有阻断性的:
    特殊类型不能简单地用继承了object就有了他的所有的相同特性.
    我猜测,编译器也一定做了一些手脚在里面.
    //object没有继承于任何类型。
    //Interface没有继承于任何类型。
    //System.Object->class
    //System.Object->System.Enum-->enum
    //System.Object->System.Array-->[]
    //System.Object->System.ValueType->struct
    //System.Object->System.Delegate->System.MulticastDelegate->delegate 
      

  7.   

    所有的结构(struct)都直接继承自 System.ValueType,后者又直接继承自 System.Object。所有的枚举类型(enum)都直接继承自 System.Enum,后者又直接继承自 System.ValueType。
    CLR 和所有的编程语言都对枚举类型给予了特殊的对待。System.ValueType 和 System.Enum 都是特殊的引用类型,struct 和 enum 分别从它们直接派生,但我们不能显式地写出这种派生关系。也就是说,struct A{} 就是隐含着 struct A : System.ValueType{},但不能显式的写上“: System.ValueType”。
    而 class B : System.ValueType{} 也是不允许的,因为引用类型不能从 System.ValueType 派生。System.Enum 的情况也是类似的。
      

  8.   

    再在回到问题上:[1]
    ReferenceEquals(obj1, obj2);
    这是一个静态发法.
    来自于System.Object类型(注意:不是对象,所以还不能拿继承来说事)
    object.ReferenceEquals(obj1, obj2); 
    这个是从object上发起的方法,与struct本身没有关系,
    所以并不证明就是struct带来的方法.
    另外:obj1, obj2虽然是结构类型的,有一个装箱的过程,所以实际上还是一个引用类型的对比.
    从案例中也能看出,obj2被装箱后,己经不是原来的对象了.
    总之:这里调用的ReferenceEquals跟结构类型没有任何的联系[2]
    从你提供的MSDN引证上,推测吧,C#没有实现struct的==
    ==与ReferenceEquals都是静态方法,
    实际上在没有==运算符重载的前提下,==与ReferenceEquals是同一个概念.
    所以我的推测是:struct并没有ReferenceEquals方法.
    [3]
    但实际上struct确实提供了自己的ReferenceEquals
    由于是静态方法,不会来自于继承关系.
    只能认为是通过object自然引用过来的.我还不能下结论,只是先提供一个思路.ReferenceEquals与==确实是有联系的,
    是什么样的联系,值得思考...
      

  9.   

    ReferenceEquals是Object的静态方法,用于比较两个引用类型的对象是否是对于同一个对象的引用。对于值类型它总是返回false。
    ==是一个可以重载的二元操作符。
      

  10.   

    ReferenceEquals是查询引用地址的,Equals只是比较值相等
      

  11.   

    CLR 支持“单实现继承”和“多接口继承”。CLR 规定一个类型只能有一个基类型,System.Object 是所有类型的最终基类型。这种继承称作“实现继承(implementation inheritance)”,因为派生类型继承了基类型的所有行为和能力。“接口继承(interface inheritance)”意味着一个类型继承的是接口中的方法签名,而非方法实现。当一个类型继承了一个接口时,它只是在许诺提供其中的方法实现;如果类型没有提供接口方法的实现,那么类型必须是抽象的,从而不可能被实例化。接口不会继承自任何的 System.Object 派生类型。接口仅仅是一个包含着一组虚方法的抽象类型,其中每一个方法都有它们的名称、参数和返回值类型。接口方法不能包括任何实现,因此接口是不完整的(抽象的)。注意接口中也可以定义事件、无参属性以及含参属性(C#中又称索引器),因为它们都只不过是映射到方法上的语法缩写而已。
      

  12.   


    不错,的确这样,谢谢,呵呵:
    ReferenceEquals对于值类型它总是返回false。
    这个是确定的,案例上也能看出来,值类型总会装箱,
    装完箱后,己经不是原来的"值"自己的对象了,
    这样的装箱,每个都是不同的地址(反例是string)
    所以地址肯定不同,ReferenceEquals也永完是false我的问题的:值类型的ReferenceEquals是哪来的?
    我是想复杂了,其实就是从object通过自然继承得来的.
    (静态方法不能继承,但可以自然继承,我还没有好好理解过,自然继承得来的静态方法)
    对比:
    潜在的问题是:==是不是就没有继承一说的,(重载的==,会引起混乱,小心点用)
      

  13.   

    呵呵,多发贴有助于理清思路,
    这就是CSDN的好外了...慢慢整理中...//相等性分为:A引用相等(ReferenceEquals),B值相等(Equals)
    //B值相等还分为:A静态Equals(Object objA, Object objB),B实例Equals(Object obj)//参考代码[1]推论
    //引用相等意味着要比较的不是两个对象,而是两个对象引用,//参考代码[2]推论
    //值相等是大家普遍理解的意义上的相等:它意味着两个对象包含相同的值。//参考代码[2]推论
    //如果两个对象具有引用相等性,则它们也具有值相等性,
    //但是值相等性不能保证引用相等性//参考代码[2]推论
    //静态版的Equals依赖于实例版的Equals//参考代码[4]推论
    //如果重载 == 相当于把系统默认的引用相等转换成值相等
    //当类型重载 operator == 时, 它也必须重写 实例版的Equals 方法以提供同样的功能。
    //重载运算符 == 的任何类型还应重载运算符 !=。//运算符 ==
    //默认情况下,通过判断两个引用是否指示同一对象来测试引用是否相等,
    //因此引用类型不需要实现运算符 == 就能获得此功能。//注意:
    //运算符 == 的重载中的常见错误是使用 (a == b)、(a == null) 或 (b == null) 来检查引用相等性。
    //这会导致调用重载的运算符 ==,从而导致无限循环。
    //应使用 ReferenceEquals 或将类型强制转换为  Object 来避免无限循环。//值类型继承ValueType,将重写实例版的Equals//建议:重写 Equals 的任何类同时也重写 System.Object.GetHashCode。//由于在.Net中不允许重写静态方法
    //所ReferenceEquals和静态Equals是不能重写的
    //对ReferenceEquals来讲,始终是自然引用,也就是来自于系统默认的对象引用比较
    //对静Equals来讲,同样是自然引用,但他调用的是来自对象本身的实例,相当于被重写了.
      

  14.   


    补充:
    对值类型使用ReferenceEquals 是永假的,哪怕两个参数是同一个变量
      

  15.   


    总结的真好,呵呵,谢谢,又可以补充了.实际上,跟本做不出这样的案例来:B b = new B();
    Console.WriteLine(B.ReferenceEquals(1,1));
    Console.WriteLine(B.ReferenceEquals(b, b));在执行前,struct类型己经被装箱了,并指向一个称为"被装箱的值类型(注:它还是归结为引为类型的)"
    public static bool ReferenceEquals(object objA, object objB);间接证明:
    所以我们执行的是object的ReferenceEquals,
    被装箱的值类型,不再是原来的"字面量"了
      

  16.   

    下面我们再看看
    结构类型的"值相等"EqualsEquals有静态与实例之分
    !!静态的Equals实质上调用了实例的Equals所以从技术讲:
    B.Equals(a,b)与B.Equals(b,a)是不同的,
    这个C#不能保证,而要我们在设计实例Equals来自律,MSDN有这样的建议.
    B.Equals(a,b)调用的是a.Equals(b),而不是b.Equals(a)有区别的.由于interface IEqualityComparer<T>使用到了
    Equals(a,b)注:没有指定是静态或实例的.
    所以不加注意,理论上会出现奇怪的现象.using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;struct B
    {
        public int k;
        int i;
        int j;    public B(int i,int j)
        {
            k = 0;
            this.i = i;
            this.j = j;
        }
    }namespace ConsoleApplication2
    {
        class Program
        {
            static void Main(string[] args)
            {
                //开始
                Console.WriteLine("-----------------------");            //测试两个结构的相等性(公有字段+私有字段)
                B b1 = new B(100, 200) { k = 300 };
                B b2 = new B(100, 200) { k = 300 };
                B b3 = new B(200, 200) { k = 300 };            B b = new B();
                Console.WriteLine(B.ReferenceEquals(1,1));
                Console.WriteLine(B.ReferenceEquals(b, b));            //相等情况(包括结构上的六大默认方法,4实例+2静态)
                Console.WriteLine("实例方法,b1==b2:{0}",b1.Equals(b2));
                //静态比较调用了:"引用比较"+"null比较"+"实例比较"
                Console.WriteLine("静态方法,b1==b2:{0}", B.Equals(b1, b2));
                Console.WriteLine("引用比较,b1==b2:{0}",B.ReferenceEquals(b1, b2));
                //结构类型的Hashcode等于结构类型第一个字段的Hashcode(现在看来也不是?!)
                Console.WriteLine("Hashcode,b1:{0},b1.k:{1}", b1.GetHashCode(), b1.k.GetHashCode());
                Console.WriteLine("GetType:{0}", b1.GetType());
                Console.WriteLine("ToString:{0}",b1.ToString());
                //至于==运算,暂认为没有就是了            //不等情况
                Console.WriteLine(b1.Equals(b3));            Console.ReadKey();
            }
        }
    }
    这里留一个新问题:
    B的比较方法:
    b1.Equals(b2)是怎么实现的呢?!
      

  17.   

    值类型的相等性总结:[1]实例的 值相等 性
    结构类型提供了默认的相等性
    (I)首先判定是否为null
    (II)其次判断类型是否相等
    (III)利用反射原理,逐项比较以下字段
    BindingFlags.Public 公共成员。
    BindingFlags.NonPublic 非公共成员(即私有成员和受保护的成员)。
    BindingFlags.Instance 实例成员.[2]:静态的 值相等 性
    (I)判"引用相等"
    (II)null=null
    (III)调用a.Euqals(b)
    注意,两个比较对象的次序不能错
    从技术讲:B.Equals(a,b)与B.Equals(b,a)是不同的,[3]ReferenceEquals 的 引用相等 性
    感谢hack95 
    现象:对于值类型它总是返回false,即便是同一对象。
    主要还是因为"装箱"的原因造成。

    [4]ReferenceEquals 的 引用相等 性

    感谢空军
    C#没有用于值类型的默认相等运算符(==)实现
      

  18.   

    30楼的[4]手滑了
    应该是:[4]== 的 引用相等 性 
    ----------------------------引用类型的相等性总结: (没有考虑特殊类型,指针,委托,数组...)
    [1]实例的 值相等 性
    就是==[2]静态的 值相等 性
    [A]==没有重载
    就是==
    [B]==重载
    (I)判"引用相等"
    (II)null=null
    (III)调用a.Euqals(b)
    与结构性相等类似,
    可以理解为把引用相等转换为值性相等[3]ReferenceEquals 的 引用相等 性
    就是==[4]== 的 引用相等 性
    ==是C#的语言特性,是根基,这个没法再验证了吧:
    C#为引用类型提供默认的相等运算符(==)实现。
    暂且认为对象的地址相同吧.判定两个引用类型的变量是否指向相同
    static bool ClassEqualsCompare<U, V>(U obj1, V obj2)
        where U:class
        where V:class
    {
        return obj1 == obj2;
    }
      

  19.   

    再论==class MyClass
    {
        static void Main(string[] args)
        {
            object obj1 = new object();
            object obj2 = new object();
            int i1 = 1;
            int i2 = 2;        bool r1 = obj1 == obj2;
            bool r2 = i1 == i2;        //bool r3 = obj1 == i1;//运算符“==”无法应用于“object”和“int”类型的操作数
            //bool r4 = i1 == obj1;//运算符“==”无法应用于“int”和“object”类型的操作数        bool r5 = obj1 == (object)i1;
            //与ReferenceEquals的效果相同
            object.ReferenceEquals(obj1, i1);
        }
    }结论:
    [1]==是原生的,ReferenceEquals非原生的,依赖于==的实现
    [2]==本质上就是一个方法,可以把形参转为实参
    [3]==是区别左操作数和右操作数的有兴趣的朋友,IL以下代码看看:
    class MyClass
    {
        static void Main( )
        {
            object obj1 = new object();
            object obj2 = new object();
            bool resutl = obj1 == obj2;
        }
    }还剩下最后一个问题没有讨论了:
    ==的重载,关于operator==
    (怀疑是两码事)
      

  20.   


    [2]==本质上就是一个方法,可以把形参转为实参 //==本质就是一个操作符而已,非要看看怎么实现的,反编译一下就会知道默认是用 ceq,具体查msdn
      

  21.   

    这个错了,重新思考一下:
    [2]==本质上就是一个方法,可以把形参转为实参 //正确的理解
    ==本质上不是一个方法
    他的左操作数和右操作数只使用转换后的值,
    而方法本身应该能够把实参转为形参bool r = obj1 == i;//失败
    bool r = object.ReferenceEquals(obj1, i);//成功
      

  22.   

    其实知道IL的CEQ的的用处也不大,
    主要还是要确定他的两个操作数.
      

  23.   

    LZ 这是我多年的积累,如下 
    Object类提供的equals()方法的实现是:判断两个对象是否指向同一个内存区域
    如果想在自定义类中测试同类对象是否相等,最好重写(覆盖) equals方法。
    在 官方提供的 一些 类中 , equals  已经重新被重写了。Equals  比内容   ==比值。
    (动态绑定)
    为什么重写?
    重写的过程: 因为 object  是java  祖先类 ,所有的类 继承了 object类。继承object类就要继承类的所有属性和方法 。   在使用 object 类中的 equals 方法的过程 ,他的  原意是 比值(即是内容相等,又是内存地址相等),
    子类 重写 equals 的过程  是 一个动态 绑定的过程 。重写之后的方法,实现了动态绑定的过程。这个时候对象 访问的 equals  是你本类equals 方法 而不是 object  方法
      

  24.   

    如果看到ceq,那就是栈上的两个值判等,如果是引用类型的话,就是引用判等(因为引用类型在栈上就是一个引用)
      

  25.   

    @vwxyzh
    谢谢呵呵,那具体是什么呢,
    比如引用类型,就是比较地址嘛?值类型,如果是复杂的嵌套类呢?
    (或干脆只比指定的类型?)
      

  26.   

    补一段Jon   Skeet的总结,比较简洁:什么时候应该使用==?什么时候应该使用Equals?(原作:Jon   Skeet)  
      
    Equals方法只是在System.Object中定义的一个虚拟方法,它由任何选择执行该任务的类所重写。
    ==运算符是一个可由类重载的运算符,该类通常具有恒等行为。  
       
    对于未重载==的引用类型,该运算符会比较两个引用类型是否引用同一对象,
    而这恰好是System.Object中的Equals实现所做的工作。  
       
    对于未重载==的值类型,该运算符会比较这两个值是否"按位"相等,即是否这两个值中的每个字段都相等。
    当您对值类型调用Equals时,仍然会发生这一情况,
    但这一次,该实现是由ValueType提供的,并且使用反射进行比较,
    从而使比较速度比特定于类型的实现慢很多。  
       
    到此为止,二者是如此类似。
    二者之间的主要区别是多态。
    运算符被重载而不是被重写,这意味着除非编译器知道调用更为具体的版本,否则它只是调用恒等版本。
    为阐明这一点,请看下面这个示例:  
       
    using System;
    public class Test
    {
        static void Main()
        {
            //   Create   two   equal   but   distinct   strings  
            string a = new string(new char[] { 'h', 'e', 'l', 'l', 'o' });
            string b = new string(new char[] { 'h', 'e', 'l', 'l', 'o' });
            Console.WriteLine(a == b);
            Console.WriteLine(a.Equals(b));
            //   Now   let's   see   what   happens   with   the   same   tests   but  
            //   with   variables   of   type   object  
            object c = a;
            object d = b;
            Console.WriteLine(c == d);
            Console.WriteLine(c.Equals(d));
        }
    }   
    结果是:  
    True  
    True  
    False  
    True  
       
    第三行是False,原因在于编译器不知道c和d的内容都是字符串引用,
    因而只能调用==的非重载版本。
    因为它们是对不同字符串的引用,所以恒等运算符返回False。
      

  27.   

    副产品:下面两个语句意义完全不同,你可以代入程序中试试。string s1 = "hello";
    string s1 = new string(new char[] { 'h', 'e', 'l', 'l', 'o' });
      

  28.   

    因为 object.ReferenceEquals 是公有的静态方法,它的两个参数都是 object 类型的,而每个类型最终都派生自 object,所以 object.ReferenceEquals 可用于任何类型,包括LZ说的值类型。
      

  29.   

    using System;public class A
    {
        //错误:类型“A”已定义了一个名为“op_Equality”的具有相同参数类型的成员
        public int i;
        public int j;    static public bool operator ==(A lhs, A rhs)
        {
            Console.WriteLine("A==A");
            return true;
        }
        static public bool operator !=(A lhs, A rhs)
        {
            Console.WriteLine("A!=A");
            return false;
        }    static public bool operator ==(A lhs, B rhs)
        {
            Console.WriteLine("A==B");
            return true;
        }
        static public bool operator !=(A lhs, B rhs)
        {
            Console.WriteLine("A!=B");
            return false;
        }    static public bool operator ==(B lhs, A rhs)
        {
            Console.WriteLine("B==A");
            return true;
        }
        static public bool operator !=(B lhs, A rhs)
        {
            Console.WriteLine("B!=A");
            return false;
        }
    }public class B
    {
        public int i;
        public int j;    static public bool operator ==(B lhs, B rhs)
        {
            Console.WriteLine("B==B");
            return true;
        }
        static public bool operator !=(B lhs, B rhs)
        {
            Console.WriteLine("B!=B");
            return false;
        }    static public bool operator ==(B lhs, A rhs)
        {
            Console.WriteLine("B==A");
            return true;
        }
        static public bool operator !=(B lhs, A rhs)
        {
            Console.WriteLine("B!=A");
            return false;
        }
    }public class Test
    {
        static void Main()
        {
            A a= new A();
            B b= new B();        Console.WriteLine(a == b);
            //错误:运算符“==”无法应用于“B”和“A”类型的操作数
            //错误:在以下方法或属性之间的调用不明确:“B.operator ==(B, A)”和“A.operator ==(B, A)”
            Console.WriteLine(b == a);
        }
    }
      

  30.   

    @空军
    MSDN中有没有提到:
    系统内置值类型的==是怎么实现的?值类型当中只有,decimal实现了==的重载
    public static bool operator ==(decimal d1, decimal d2);
    其它系统内置值类型并没有实现==的重载[1]默认情况下,struct是没有==的
    [2]系统内置值类型没有实现==的重载(除decimal)
    [3]自定义的struct,如果没有实现==的重载,也是不能进行==比较的.
      

  31.   

    关于:操作符重载 [1]操作符就是方法?!
    操作符是C#中用于定义类的实例对象间表达式操作的一种成员。
    操作符仍然是对方法实现的一种逻辑界面抽象,
    也就是说在编译成的IL中间语言代码中,操作符仍然是以方法的形式调用的。[2]操作符重载(1)
    在类内定义操作符成员又叫操作符重载。
    C#中的重载操作符共有三种:一元操作符,二元操作符和转换操作符。
    并不是所有的操作符都可以重载,三种操作符都有相应的可重载操作符集,列于下表: 
    [A]一元操作符 + - ! ~ ++ -- true false
    [B]二元操作符 + - * / % & | ^ << >> == != > < >= <=
    [C]转换操作符 隐式转换()和显式转换() [3]操作符重载(2)
    重载操作符必须是public和static 修饰的,否则会引起编译错误,这在操作符的逻辑语义下是不言而喻的。
    父类的重载操作符会被子类继承,但这种继承没有覆盖,隐藏,抽象等行为,
    不能对重载操作符进行virtual sealed override abstract修饰。
    操作符的参数必须为传值参数。[4]操作符重载要求(1)
    我们在返回值时,往往需要“new”一个新的变量--除了true和false操作符。
    一元操作符中只有一个参数。
    操作符“++”和“--”返回值类型和参数类型必须和声明该操作符的类型一样。
    操作符“+ - ! ~”的参数类型必须和声明该操作符的类型一样,返回值类型可以任意。
    true和false操作符的参数类型必须和声明该操作符的类型一样,而返回值类型必须为bool,而且必须配对出现--也就是说只声明其中一个是不对的,会引起编译错误。
    参数类型的不同会导致同名的操作符的重载--实际上这是方法重载的表现。[4]操作符重载要求(2)
    二元操作符参数必须为两个,而且两个必须至少有一个的参数类型为声明该操作符的类型
    返回值类型可以任意。
    有三对操作符也需要必须配对声明出现,它们是 “==”和“!=”,“>”和“<”,“>=”和“<=”。
    需要注意的是两个参数的类型不同,虽然类型相同但顺序不同都会导致同名的操作符的重载。[5]操作符重载要求(3)
    转换操作符为不同类型之间提供隐式转换和显式转换,主要用于方法调用,转型表达和赋值操作。
    转换操作符对其参数类型(被转换类型)和返回值类型(转换类型)也有严格的要求。
    参数类型和返回值类型不能相同,且两者之间必须至少有一个和定义操作符的类型相同。
    转换操作符必须定义在被转换类型或转换类型任何其中一个里面。
    不能对系统定义过的转换操作进行重新定义。
    两个类型也都不能是object或接口类型,两者之间不能有直接或间接的继承关系--这三种情况系统已经默认转换。
      

  32.   

    参考语种:重载、转换和操作符
     
    操作符的重载确认遵循常见的三个步骤:
    [1]选择候选函数;
    [2]选择可行函数,包括识别每个实参的潜在转换序列;
    [3]选择最佳匹配的函数。
     
    一般而言,候选函数集由所有与被使用的函数同名的函数构成,被使用的函数可以从函数调用处看到。
    对于操作符用在表达式中的情况,候选函数包括操作符的内置版本以及该操作符的普通非成员版本。
    另外,如果左操作数具有类类型,而且该类定义了该操作符的重载版本,则候选集将包含操作符的重载版本。
     
    正确设计类的重载操作符、转换函数和转换构造函数需要多加小心。
    尤其是,如果类既定义了转换操作符又定义了重载操作符,容易产生二义性。下面是一些经验法则供参考:
    [1]不要定义相互转换的类,即如果类Foo具有接受类Bar的对象的构造函数,不要再为类Bar定义到类型Foo的转换操作符。
    [2]避免到内置算术类型的转换。具体而言主,如果定义了到算术类型的转换,则:
    -->不要定义接受算术类型的操作符的重载版本。
    -->如果用户需要使用这些操作符,转换操作符将转换你所定义的类型的对象,然后可以使用内置操作符。
    -->不要定义转换到一个以上算术类型的转换。让标准转换提供到其他算术类型的转换。
      

  33.   

    @狂奔中的蜗牛问的好,
    原因在于编译器不知道c和d的内容都是字符串引用, 
    这个解释有一定的倒理,但还没有落在根本上,
    问题的关键还在于==运算符的重载判定原则,
    具体见60楼和61楼作一点总结如下:
    [1]重载规则(重载规则虽然不是判定规则,但能够提供一些约束条件)
    ==重载,左/右参数,至少有一个是定义该重载的类型(一个或两个)
    ==重载,左/右参数不等的情况下,A,B与B,A是不同的==重载
    ==重载,在一个对象中的定义无非是三种情况:A==A,A==B,B==A
    [2]重载规则总突
    ==重载冲突的判定地点,在调用对象的可见范围内
    交叉对象中的==重载可能出现同时定义相同的A==B类型重载,在编译时提示冲突
    [3]判定规则(推测,借鉴C++语言)
    [A]选择候选函数;
    [B]选择可行函数,包括识别每个实参的潜在转换序列;
    [C]选择最佳匹配的函数。
    [D]如果左操作数具有类类型,而且该类定义了该操作符的重载版本,则候选集将包含操作符的重载版本。
    [E]候选函数集由所有与被使用的函数同名的函数构成,被使用的函数可以从函数调用处看到。
    [F]对于操作符用在表达式中的情况,候选函数包括操作符的内置版本以及该操作符的普通非成员版本。
    class A
    {
        //A vs A
        public static bool operator ==(A lhs, A rhs)
        {
            System.Console.WriteLine("A == A");
            return true;
        }
        public static bool operator ==(A lhs, A rhs)
        {
            System.Console.WriteLine("A != A");
            return false;
        }    //A vs B
        public static bool operator ==(A lhs, B rhs)
        {
            System.Console.WriteLine("A == B");
            return true;
        }
        public static bool operator ==(A lhs, B rhs)
        {
            System.Console.WriteLine("A != B");
            return false;
        }    //B vs A
        public static bool operator ==(B lhs, A rhs)
        {
            System.Console.WriteLine("B == A");
            return true;
        }
        public static bool operator ==(B lhs, A rhs)
        {
            System.Console.WriteLine("B != A");
            return false;
        }
    }class B
    {
        //TODO:
    }在本案例中就非常明确了.
    ==重载有以下两种候远种情况:
    string == string (string 本身对==做了重载)
    objcet == objcet
    c== d 最合适的就是 objcet == objcet
    object的==是比较引用,实际上的"地址(不考虑GC)"的比较顺便说明一下,扩展方法是不能用在operator上的,只能从类/结构内部来定义...
      

  34.   

    http://www.kuqin.com/dotnet/20090405/44350.html4.1 值类型判等    * Equals,System.ValueType重载了System.Object的Equals方法,用于实现对实例数据的判等。
        * ReferenceEquals,对值类型应用ReferenceEquals将永远返回false。
        * ==,未重载的==的值类型,将比较两个值是否“按位”相等。红色标注部分,不知道是个啥意思!!!
      

  35.   

    对于基本类型。==和equeals是相同的啊,然而对于对象来说,==是比较两个对象是否是同一个对象,equeals是比较两个对象的内容是否相同,此文参考Thinking in java。
      

  36.   


    建议看看 Jeffrey Richter 的《Microsoft .NET 框架程序设计(修订版)》(李建忠译)第157-161页:6.1.3 为值类型实现 Equals 方法
    6.1.4 Equals 方法与 == / !== 操作符的实现总结
      

  37.   

    Jeffrey Richter 的《Microsoft .NET 框架程序设计(修订版)》 是基于 .net framework 1.1 的,
    同一作者的《框架设计(第2版) CLR Via C#》是基于 .net framework 2.0 的,建议看后者。
      

  38.   

    终于可以结贴了。我一直是猜测是这样,只是没有办法通过代码去验证,呵呵。 编译器认为的基元类型:和使用操作符一样来调用它们的Equals方法
      

  39.   

    @空军下面这个问题有定论嘛?如何获得一个class类型类量的指针
    http://topic.csdn.net/u/20090905/09/85f03475-1801-42a1-b7ba-fd7c5c4987ae.html
      

  40.   


    sorry,这个问题没有研究过。
      

  41.   

    @空军好象还不是这个意思,因为Equals调用了==
    网上查不到:6.1.3 为值类型实现 Equals 方法 
    不知道是怎么说的?给一个Int32的源码
    /*
     * Int32.cs - Implementation of the "System.Int32" class.
     *
     * Copyright (C) 2001  Southern Storm Software, Pty Ltd.
     *
     * This program is free software; you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation; either version 2 of the License, or
     * (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     *
     * You should have received a copy of the GNU General Public License
     * along with this program; if not, write to the Free Software
     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     */namespace System
    {using System.Private;
    using System.Private.NumberFormat;
    using System.Globalization;#if !ECMA_COMPAT && CONFIG_FRAMEWORK_2_0
    using System.Runtime.InteropServices;[ComVisible(true)]
    [Serializable]
    #endif
    public struct Int32 : IComparable, IFormattable
    #if !ECMA_COMPAT
            , IConvertible
    #endif
    #if CONFIG_FRAMEWORK_2_0
            , IComparable<int>, IEquatable<int>
    #endif
    {
            private int value_;        public const int MaxValue = 0x7FFFFFFF;
            public const int MinValue = unchecked((int)(-0x80000000));        // Override inherited methods.
            public override int GetHashCode()
                            { return (value_); }
            public override bool Equals(Object value)
                            {
                                    if(value is Int32)
                                    {
                                            return (value_ == ((Int32)value).value_);
                                    }
                                    else
                                    {
                                            return false;
                                    }
                            }        // String conversion.
            public override String ToString()
                            {
                                    return ToString(null, null);
                            }
            public String ToString(String format)
                            {
                                    return ToString(format, null);
                            }
            public String ToString(IFormatProvider provider)
                            {
                                    return ToString(null, provider);
                            }
            public String ToString(String format, IFormatProvider provider)
                            {
                                    return Formatter.FormatInt32( value_, format, provider );
                            }        // Parsing methods.
            public static int Parse(String s, NumberStyles style,
                                                            IFormatProvider provider)
                            {
                                    NumberParser.ValidateIntegerStyle(style);
                                    return NumberParser.ParseInt32
                                            (s, style, NumberFormatInfo.GetInstance(provider), 0);
                            }
            public static int Parse(String s)
                            {
                                    return Parse(s, NumberStyles.Integer, null);
                            }
            public static int Parse(String s, IFormatProvider provider)
                            {
                                    return Parse(s, NumberStyles.Integer, provider);
                            }
            public static int Parse(String s, NumberStyles style)
                            {
                                    return Parse(s, style, null);
                            }        // Implementation of the IComparable interface.
            public int CompareTo(Object value)
                            {
                                    if(value != null)
                                    {
                                            if(!(value is Int32))
                                            {
                                                    throw new ArgumentException(_("Arg_MustBeInt32"));
                                            }
                                            int temp = ((Int32)value).value_;
                                            if(value_ < temp)
                                            {
                                                    return -1;
                                            }
                                            else if(value_ > temp)
                                            {
                                                    return 1;
                                            }
                                            else
                                            {
                                                    return 0;
                                            }
                                    }
                                    else
                                    {
                                            return 1;
                                    }
                            }#if CONFIG_FRAMEWORK_2_0        // Implementation of the IComparable<int> interface.
            public int CompareTo(int value)
                            {
                                    return (value_ - value.value_);
                            }        // Implementation of the IEquatable<int> interface.
            public bool Equals(int obj)
                            {
                                    return (value_ == obj.value_);
                            }#endif // CONFIG_FRAMEWORK_2_0#if !ECMA_COMPAT        // Implementation of the IConvertible interface.
            public TypeCode GetTypeCode()
                            {
                                    return TypeCode.Int32;
                            }
            bool IConvertible.ToBoolean(IFormatProvider provider)
                            {
                                    return Convert.ToBoolean(value_);
                            }
            byte IConvertible.ToByte(IFormatProvider provider)
                            {
                                    return Convert.ToByte(value_);
                            }
            sbyte IConvertible.ToSByte(IFormatProvider provider)
                            {
                                    return Convert.ToSByte(value_);
                            }
            short IConvertible.ToInt16(IFormatProvider provider)
                            {
                                    return Convert.ToInt16(value_);
                            }
            ushort IConvertible.ToUInt16(IFormatProvider provider)
                            {
                                    return Convert.ToUInt16(value_);
                            }
            char IConvertible.ToChar(IFormatProvider provider)
                            {
                                    return Convert.ToChar(value_);
                            }
            int IConvertible.ToInt32(IFormatProvider provider)
                            {
                                    return value_;
                            }
            uint IConvertible.ToUInt32(IFormatProvider provider)
                            {
                                    return Convert.ToUInt32(value_);
                            }
            long IConvertible.ToInt64(IFormatProvider provider)
                            {
                                    return Convert.ToInt64(value_);
                            }
            ulong IConvertible.ToUInt64(IFormatProvider provider)
                            {
                                    return Convert.ToUInt64(value_);
                            }
            float IConvertible.ToSingle(IFormatProvider provider)
                            {
                                    return Convert.ToSingle(value_);
                            }
            double IConvertible.ToDouble(IFormatProvider provider)
                            {
                                    return Convert.ToDouble(value_);
                            }
            Decimal IConvertible.ToDecimal(IFormatProvider provider)
                            {
                                    return Convert.ToDecimal(value_);
                            }
            DateTime IConvertible.ToDateTime(IFormatProvider provider)
                            {
                                    throw new InvalidCastException
                                            (String.Format
                                                    (_("InvalidCast_FromTo"), "Int32", "DateTime"));
                            }
            Object IConvertible.ToType(Type conversionType, IFormatProvider provider)
                            {
                                    return Convert.DefaultToType(this, conversionType,
                                                                                             provider, true);
                            }#endif // !ECMA_COMPAT}; // class Int32}; // namespace System
      

  42.   

    见:http://topic.csdn.net/u/20090911/17/be24c02e-0eed-41fe-bc17-e7f643c473ce.html
      

  43.   

    还是看原版的好,说的很清楚:Compiler primitive types  Your compiler will provide implementations of the == and !=
    operators for types that it considers primitives. For example, the C# compiler knows how to
    compare Object, Boolean, Char, Int16, Uint16, Int32, Uint32, Int64, Uint64, Single,
    Double, Decimal, and so on for equality. In addition, these types provide implementations of
    Equals, so you can call this method as well as use operators.应该翻译成:
    编译器为内置类型实现了==和!=运算Decimal应该排除在外吧?!
    (这个是重载过的,难道也这样?!)