背景
我在当前项目上使用基于接口的编程,并且在重载运算符(特别是Equality和Inequality运算符)时遇到了问题。
假设条件
- 我正在使用C#3.0,.NET 3.5和Visual Studio 2008
更新-以下假设是错误的!
- 要求所有比较均使用Equals而不是operator ==并不是一个可行的解决方案,尤其是在将类型传递给库(例如Collections)时。
我担心要求使用Equals而不是operator ==的原因是,我在.NET指南中找不到它说它将使用Equals而不是operator ==甚至没有建议的地方。但是,在重新阅读《覆盖等于和运算符==的准则》之后,我发现了这一点:
默认情况下,运算符==通过确定两个引用是否指示同一对象来测试引用是否相等。因此,引用类型不必实现运算符==即可获得此功能。当类型是不可变的,即实例中包含的数据不能更改时,重载运算符==以比较值相等而不是引用相等是有用的,因为作为不可变对象,它们可以被认为与因为它们具有相同的价值。在非不可变类型中覆盖运算符==不是一个好主意。
这个平等的接口
当使用诸如Contains,IndexOf,LastIndexOf和Remove这样的方法测试是否相等时,IEquatable接口将由Dictionary,List和LinkedList等通用集合对象使用。应该为可能存储在通用集合中的任何对象实现它。
制约因素
- 任何解决方案都不必要求将对象从其接口转换为具体类型。
问题
- 当operator ==的两端都是接口时,没有底层具体类型的operator ==重载方法签名匹配,因此将调用默认的Object operator ==方法。
- 在类上重载运算符时,二进制运算符的至少一个参数必须为包含类型,否则会生成编译器错误(错误BC33021 http://msdn.microsoft.com/zh-cn/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.