Zac*_*ame 72 .net c# equals operator-overloading
我在当前项目中使用基于接口的编程,并在重载运算符(特别是Equality和Inequality运算符)时遇到问题.
更新 - 以下假设是错误的!
我担心要求使用Equals而不是operator ==的原因是我在.NET指南中找不到任何地方,它声称它会使用Equals而不是operator ==甚至建议它.但是,重新阅读覆盖等于和操作员指南后==我发现了这个:
默认情况下,operator ==通过确定两个引用是否指示同一对象来测试引用相等性.因此,引用类型不必实现operator ==以获得此功能.当一个类型是不可变的,也就是说,实例中包含的数据不能改变时,重载operator ==来比较值的相等而不是引用相等可能是有用的,因为作为不可变对象,它们可以被认为是相同的因为它们具有相同的价值.在非不可变类型中覆盖operator ==不是一个好主意.
和这个Equatable接口
当在Contains,IndexOf,LastIndexOf和Remove等方法中测试相等性时,IEquatable接口由泛型集合对象(如Dictionary,List和LinkedList)使用.它应该针对可能存储在泛型集合中的任何对象实现.
请参阅下面的代码和输出,以说明问题.
在使用基于接口的编程时,如何为类提供适当的操作符重载?
对于预定义的值类型,如果操作数的值相等,则相等运算符(==)返回true,否则返回false.对于除string之外的引用类型,如果其两个操作数引用同一对象,则==返回true.对于字符串类型,==比较字符串的值.
using System;
namespace OperatorOverloadsWithInterfaces
{
public interface IAddress : IEquatable<IAddress>
{
string StreetName { get; set; }
string City { get; set; }
string State { get; set; }
}
public class Address : IAddress
{
private string _streetName;
private string _city;
private string _state;
public Address(string city, string state, string streetName)
{
City = city;
State = state;
StreetName = streetName;
}
#region IAddress Members
public virtual string StreetName
{
get { return _streetName; }
set { _streetName = value; }
}
public virtual string City
{
get { return _city; }
set { _city = value; }
}
public virtual string State
{
get { return _state; }
set { _state = value; }
}
public static bool operator ==(Address lhs, Address rhs)
{
Console.WriteLine("Address operator== overload called.");
// If both sides of the argument are the same instance or null, they are equal
if (Object.ReferenceEquals(lhs, rhs))
{
return true;
}
return lhs.Equals(rhs);
}
public static bool operator !=(Address lhs, Address rhs)
{
return !(lhs == rhs);
}
public override bool Equals(object obj)
{
// Use 'as' rather than a cast to get a null rather an exception
// if the object isn't convertible
Address address = obj as Address;
return this.Equals(address);
}
public override int GetHashCode()
{
string composite = StreetName + City + State;
return composite.GetHashCode();
}
#endregion
#region IEquatable<IAddress> Members
public virtual bool Equals(IAddress other)
{
// Per MSDN documentation, x.Equals(null) should return false
if ((object)other == null)
{
return false;
}
return ((this.City == other.City)
&& (this.State == other.State)
&& (this.StreetName == other.StreetName));
}
#endregion
}
public class Program
{
static void Main(string[] args)
{
IAddress address1 = new Address("seattle", "washington", "Awesome St");
IAddress address2 = new Address("seattle", "washington", "Awesome St");
functionThatComparesAddresses(address1, address2);
Console.Read();
}
public static void functionThatComparesAddresses(IAddress address1, IAddress address2)
{
if (address1 == address2)
{
Console.WriteLine("Equal with the interfaces.");
}
if ((Address)address1 == address2)
{
Console.WriteLine("Equal with Left-hand side cast.");
}
if (address1 == (Address)address2)
{
Console.WriteLine("Equal with Right-hand side cast.");
}
if ((Address)address1 == (Address)address2)
{
Console.WriteLine("Equal with both sides cast.");
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
Address operator== overload called
Equal with both sides cast.
Run Code Online (Sandbox Code Playgroud)
Joh*_*lla 57
简短的回答:我认为你的第二个假设可能存在缺陷.Equals()是检查两个对象的语义相等性的正确方法,而不是operator ==.
答案很长:操作符的重载分辨率是在编译时执行的,而不是在运行时执行的.
除非编译器能够明确地知道它应用运算符的对象的类型,否则它将无法编译.由于编译器无法确定某个IAddress是否具有已==定义的覆盖,因此它将回退到默认operator ==实现System.Object.
要更清楚地看到这一点,请尝试定义operator +for Address和添加两个IAddress实例.除非你明确地转换为Address,否则它将无法编译.为什么?因为编译器不能告诉某个特定IAddress是一个Address,并且没有默认operator +实现可以回归到System.Object.
你的沮丧部分可能源于Object实现一个事实operator ==,而且一切都是Object,所以编译器可以成功地解决a == b所有类型的操作.当你覆盖时==,你期望看到相同的行为,但没有,这是因为编译器可以找到的最佳匹配是原始Object实现.
要求所有比较使用Equals而不是operator ==不是一个可行的解决方案,尤其是在将类型传递给库(例如Collections)时.
在我看来,这正是你应该做的.Equals()是检查两个对象的语义相等性的正确方法.有时语义相等只是引用相等,在这种情况下,您不需要更改任何内容.在其他情况下,如在您的示例中,您将Equals在需要更强的平等契约而不是引用相等时覆盖.例如,Persons如果他们具有相同的社会安全号码,您可能需要考虑两个相等,Vehicles如果他们具有相同的VIN ,则可以考虑两个相等.
但是,Equals()和operator ==不一样的东西.每当你需要覆盖时operator ==,你应该覆盖Equals(),但几乎从不反过来.operator ==更多的是语法上的便利.某些CLR语言(例如Visual Basic.NET)甚至不允许您覆盖相等运算符.