C#相等比较
相等的比较:
值相等和引用相等。
值相等:两个值在某种意义上是想等的。
引用相等:两个引用指向完全相同的对象。
默认情况下:
值类型使用值相等。
引用类型使用引用相等。
1.标准等值比较协议
- ==和!=
- object对象Equals虚方法
- IEquatble<T>接口
==和!=
很多的例子中都使用了标准的==和!=运算符进行相等/不相等的比较。而==与!=的特殊性在于它们的运算符,因此它们是静态解析的(实际上,它们的本身就是静态函数)。因此使用==和!=时,c#会在编译时根据类型确认时哪一个函数执行比较操作,且这里没有任何虚行为。
{ int x = 5; int y = 5; Console.WriteLine(x==y);//true //这个例子编译器会把==绑定在int类型上,int是值类型,所以此时==进行的是值类型比较,所有为true } { object x = 5; object y = 5; Console.WriteLine(x == y);//false //这个例子编译器会把==绑定在object类型上,object是引用类型,所以此时==进行的是引用比较,所有为false }
Object对象Equals虚方法
Equals定义在System.Object上的方法,所以所有的类型都支持这个方法。
Equals在运行时根据对象的实例类型解析的。如果你这个是对象是int类型的那么它实际调用的是int的Equals方法。对于引用类型Equals默认进行引用相等比较,而对于结构体,Equals会对结构体的每一个字段的Equals进行结构化比较。
{ object x = 5; object y = 5; Console.WriteLine(x.Equals(y));//false }
object.Equals静态方法
object 的类提供一个静态的辅助方法,该方法正是实现上一个例子中的AreEqual操作。虽然它们的名字与虚方法相同,但是它们不会冲突。
ps:这个方法可以提供null值的相等比较
object x = 1, y = 2; object.Equals(x, y); x = null; object.Equals(x, y); y = null; object.Equals(x, y);
object.Equals在泛型中的应用
public class Test<T> { T _value; public void SetValue(T value) { if (!object.Equals(value,_value)) { _value = value; } } }
object.ReferenceEquals
这个方法通常用于“自比”
StringBuilder builder1 = new StringBuilder(); StringBuilder builder2 = new StringBuilder(); Console.WriteLine(object.ReferenceEquals(builder1, builder1));//true Console.WriteLine(object.ReferenceEquals(builder1, builder2));//false
IEquatable<T>接口
将 IEquatable<T> 接口的类型参数替换为实现此接口的类型。 如果实现 IEquatable<T>,还应覆盖 Equals(Object) 和 GetHashCode() 的基类实现,使其行为与 Equals(T) 方法的行为一致。 如果确实要重写 Equals(Object),则也会在对类的静态 Equals(System.Object, System.Object)
方法的调用中调用重写的实现。 此外,还应重载 ==
和 !=
运算符。这可以确保相等性的所有测试都返回一致的结果。
2.相等比较和自定义比较
默认的相等比较
- 值类型采用相等比较
- 引用类型采用引用相等比较
此外结构体的Equals方法默认采用的是结构体值相等。(它会比较结构体中的每个字段)
说到自定义比较有两种情况比较适用
- 改变相等比较的含义
- 提高结构相等比较的速度
ps:结构体默认的相等比较算法是比较慢的。通过重载Equals能够获得近20%的性能提升,重载==运算符并实现IEquatable<T>接口可以避免相等比较过程中的装箱操作,这样可以将速度再提高20%。
平常开发中用的结构体,都做到了上述优化的,比如说Datetime。
那么该如何重写相等语义呢。
- GetHashCode和Equals必须实现
- 重载==和!=(可选)
- 实现IEquatable<T>(可选)
例子:
public struct Area : IEquatable<Area> { public readonly int Measure1; public readonly int Measure2; public Area(int m1,int m2) { Measure1 = Math.Min(m1, m2); Measure2 = Math.Max(m1, m2); } public override bool Equals(object obj) { if (!(obj is Area)) return false; return Equals((Area)obj); } /// <summary> /// 由Json Bloch推荐的模式 /// /// </summary> /// <remarks> /// int hash=17; /// hash=hash*31+field1.GetHashCode(); /// hash=hash*31+field2.GetHashCode(); /// return hash; /// </remarks> /// <returns></returns> public override int GetHashCode() => Measure1 + Measure2 * 31; public override string ToString() { return (Measure1 + Measure2).ToString(); } public bool Equals(Area other) => Measure1 == other.Measure1 && Measure2 == other.Measure2; /// <summary> /// 重载运算符 /// </summary> /// <param name="a1"></param> /// <param name="a2"></param> /// <returns></returns> public static bool operator ==(Area a1, Area a2) => a1.Equals(a2); public static bool operator !=(Area a1, Area a2) => !a1.Equals(a2); }
测试
Area area = new Area(5,3); Area area2 = new Area(3, 5); Console.WriteLine(area.Equals(area2));//true Console.WriteLine(area == area2);//true
相等比较在我们日常开发中用到地方还是很多的,至于怎么用的更好,我希望这个博客能够帮到自己或者看到这篇博客有收获的人。