背景
我在当前项目中使用基于接口的编程,并且在重载运算符(特别是 Equality 和 Inequality 运算符)时遇到了问题。
假设
- 我正在使用 C# 3.0、.NET 3.5 和 Visual Studio 2008
更新 - 以下假设是错误的!
- 要求所有比较都使用 Equals 而不是 operator== 不是一个可行的解决方案,尤其是在将类型传递给库(例如 Collections)时。
我担心要求使用 Equals 而不是 operator== 的原因是,我在 .NET 指南中找不到任何地方表明它将使用 Equals 而不是 operator== 甚至建议使用它。但是,在重新阅读了覆盖等于和运算符 == 的指南后,我发现了这一点:
默认情况下,运算符 == 通过确定两个引用是否指示同一个对象来测试引用是否相等。因此,引用类型不必实现 operator == 即可获得此功能。当类型是不可变的,即实例中包含的数据不能更改时,重载运算符 == 来比较值相等而不是引用相等可能很有用,因为作为不可变对象,它们可以被视为与 long 相同因为它们具有相同的价值。在非不可变类型中覆盖 operator == 不是一个好主意。
IEquatable 接口由通用集合对象(例如 Dictionary、List 和 LinkedList)在 Contains、IndexOf、LastIndexOf 和 Remove 等方法中测试相等性时使用。应该为可能存储在通用集合中的任何对象实现它。
约束
- 任何解决方案都不得要求将对象从它们的接口转换为它们的具体类型。
问题
- 当 operator== 的两边都是接口时,没有来自底层具体类型的 operator== 重载方法签名将匹配,因此将调用默认的 Object operator== 方法。
- 在类上重载运算符时,二元运算符的至少一个参数必须是包含类型,否则会产生编译器错误(错误 BC33021 http://msdn.microsoft.com/en-us/library/watt39ff .aspx )
- 无法在接口上指定实现
请参阅下面演示该问题的代码和输出。
问题
在使用基于接口的编程时,如何为类提供适当的运算符重载?
参考
对于预定义的值类型,相等运算符 (==) 如果其操作数的值相等则返回 true,否则返回 false。对于字符串以外的引用类型,== 如果它的两个操作数引用同一个对象,则返回 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.");
}
}
}
}
输出
Address operator== overload called
Equal with both sides cast.