C#中关于“相等”的测试

相等与否的判定在任何一门语言中都很是很重要的一部分,在决策、分支问题上意义重大。
本文针对C#语言中的相等技术进行测试,如果需要更详细的了解,可以阅读本文的参考博文(见文章最后)。

C#中提供的四种比较技术

  • ReferenceEquals()
  • 运算符比较 == !=
  • static Equals()
  • instance Equals()

ReferenceEquals()

C#中的对象分为引用类型和值类型,因此相等也可以分为两种,值类型相等(两个值类型的变量在他们的类型和内容都相同时应该是相等)和引用类型相等(两个引用类型的变量在引用同一个对象时是相等的)。

  • 无论值类型还是引用类型,ReferenceEquals()方法比较的总是两者的对象ID是否一致,即两者是不是同一个对象。
  • 针对值类型使用ReferenceEquals,结果永远都是false,因为该方法要求传入两个引用对象,这里会对两个值类型的对象分别进行装箱操作,得到的两个引用自然不同
  • 该方法实现的功能和他的名称保持一致,即比较两个变量的对象id是否一致,不存在重新定义的需求。
static void Main(string[] args)
{
    //值类型
    int a = 20, b = 20;
    //false: 虽然值相同,但两者具有不同的对象ID
    Console.WriteLine(Object.ReferenceEquals(a, b));
    //false:对于值类型使用ReferenceEquals,总会返回false,即使比较同一个变量
    //因为该方法要求两个引用对象,这里会对两个值分别进行装箱操作,得到两个引用自然不同
    Console.WriteLine(Object.ReferenceEquals(a, a));


     //引用类型
     object c = 10;
     object d = 10;
     //false:两个引用不指向同一个对象
     Console.WriteLine(Object.ReferenceEquals(c, d));
     //true: 指向同一个对象
     Console.WriteLine(Object.ReferenceEquals(c, c));
}

运行结果:
在这里插入图片描述

运算符比较 == !=

  • 默认情况下,值类型的数据使用值类型相等,引用类型使用引用类型相等。
  • 涉及到重写问题,可以参考 instance Equals方法
static void Main(string[] args)
{
     //值类型比较:默认比较两个值是否相同
     int x = 5;
     int y = 5;
     //由于两次装箱操作,基本无意义 永远都是false
    Console.WriteLine(" x,y 两个变量是否具有相同的对象ID:" + Object.ReferenceEquals(x, y));
    //True
    Console.WriteLine(" x,y 两个变量是否值类型相等:" + (x == y));


      //装箱,引用类型比较:默认比较两个引用是否指向同一个对象
      object a = 5;
      object b = 5;
      //false:不是同一个对象
      Console.WriteLine(" a,b 两个变量是否具有相同的对象ID:" + Object.ReferenceEquals(a, b));
      //false:不是同一个对象
      Console.WriteLine(" a,b 两个变量是否引用类型相等:" + (a == b));
}

static Equals(object left,object right)

  • 在c#中,object类是所有类型的基类,因此可以传入值类型,也可以传入引用类型
  • 使用时机:当不清楚两个参数的运行类型时使用
  • 该方法仅提供了一个比较的思路,不涉及具体的比较,因此不涉及重写问题。
  • 无论值类型还是引用类型,都默认实现了GetHashCode方法,所以你不用重写这个方法,除非你需要重写Equals方法。(因此,如果你重写了GetHashCode方法,那么你肯定是需要重写Equals方法)。
  • 具体实现原理如下代码所示:
public  static bool Equals(object left,object right)
{
   //值类型比较值类型,引用类型比较引用类型
   if (left == right)
      return true;

   //检查是否有值为null,一旦存在null,永远返回false
   if (left == null || right == null)
      return false;
   //使用instance Equals方法
   return left.Equals(right);
}

相等的数学性质:自反性,对称性,传递性

在讨论 instance Equals之前,必须明确相等具有哪些数学性质。

  • 自反性:一个对象必须永远是等于自己的,a==a 必须成立
  • 对称性:如果a==b为真,那么b == a也必须为真。
  • 传递性:如果a == b,b==c,那么a == c必须成立。

instance Equals()

  • 当该函数的默认行为与开发者所需要的判定方式不一致时,需要进行自定义,有重写需求。
  • 如果没有重写,相等的判断方式与ReferenceEquals一致
  • 值类型是不同的,值类型没有重载Object.Equals,ValueType.Equals()实现这一机制。
static void Main(string[] args)
{
     //值类型一般需要比较的都是值是否相等,ValueType.Equals()实现这一个功能
     int x = 5;
     int y = 5;
     Console.WriteLine(x.Equals(y));

     //默认情况下,针对引用类型,处理机制与ReferenceEquals保持一致
     //运行结果:false
     Test my01 = new Test(10);
     Test my02 = new Test(20);
     Console.WriteLine(my01.Equals(my02));


     //StringBuilder进行了重写,自定义了自己的判断机制
     //运行结果:true
     StringBuilder text01 =new StringBuilder("abc") ;
     StringBuilder text02 = new StringBuilder("abc");
     Console.WriteLine(text01.Equals(text02));
}
public class Test
{
    private int a;
    public Test(int x)
       {
           a = x;
       }
}

运行结果:
在这里插入图片描述

重载自己的instance Equals()的要求和标准模式

  • Equals方法不应该也不能抛出异常。两个变量要么相等,要么不等,没有其他结果。
  • 需要检查传出的值是否为null
  • 如果对象Id一致,直接判定相等,自反性原则。
  • 判定数据类型是否相同
  • 自定义的判断逻辑
public override bool Equals(object right)
{
     //检查是否为空
      if (right == null)
         return false;

     //检查是否为同一对象
     if (object.ReferenceEquals(this, right))
         return true;
     //类型检查
     if (this.GetType() != right.GetType())
         return false;
     //自定义比较方法,因为涉及到派生概念,所以需要使用as做一次转换
     return CompareMethod(this, right as Foo);
}

IEquatable

为了保持完整性,建议在重写Equals方法时,同时实现IEquatable接口。接口方法的结果应当与自定义重写后Equals方法的结果一致。如果你已经重写了Equals方法,那么实现IEquatable不需要额外的实现代码(直接调用Equlas方法即可)

internal class Staff : IEquatable<Staff>
{
    public string FirstName { get; set; }

    // implements IEquatable<Staff>
    public bool Equals(Staff other)
    {
        return this.FirstName.Equals(other.FirstName);
    }

    // override Equals
    public override bool Equals(object obj)
    {
        if (obj == null)
            return this == null;

        if (!(obj is Staff))
            return false;

        Staff s = obj as Staff;

        return this.FirstName == s.FirstName;
    }

    // override GetHashCode
    public override int GetHashCode()
    {
        return this.FirstName.GetHashCode();
    }            
}

参考文章

  1. Effective C# 中文版 改善C#程序的50种方法(旧版):“原则9:明白几个相等运算之间的关系"
  2. C#相等性比较
  3. C#之相等比较(常规比较)
  4. C#比较两个对象是否相等(深度比较)
  5. 浅析C#中的IEquatable接口
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值