出身背景
我在当前项目中使用基于接口的编程,在重载运算符(特别是等式和不等式运算符)时遇到问题.
假设
- 我用的是C#3.0.NET 3.5和Visual Studio 2008
更新-以下假设是错误的!
- 要求所有比较都使用Equals而不是operator==不是一个可行的解决方案,尤其是在将类型传递给库(例如集合)时.
我之所以担心需要使用等于而不是运算符==是因为我在.NET指南,声明将使用等于而不是运算符==或甚至建议使用它.然而,在重读Guidelines for Overriding Equals and Operator==之后,我发现:
默认情况下,运算符==通过确定两个引用是否指示同一对象来测试引用相等性.因此,引用类型不必实现运算符==即可获得此功能.当类型是不可变的,即实例中包含的数据不能更改时,重载运算符==以比较值相等而不是引用相等可能很有用,因为作为不可变对象,只要它们具有相同的值,就可以认为它们是相同的.在非不可变类型中重写运算符==不是一个好主意.
测试CONTAINS、IndexOf、LastIndexOf和Remove等方法中的等价性时,Dictionary、List和LinkedList等泛型集合对象使用IEquatable接口.应该为可能存储在泛型集合中的任何对象实现它.
违禁品
- 任何解决方案都不能要求将对象从其接口转换为其具体类型.
问题
- 当运算符==的两边都是接口时,底层具体类型的运算符==重载方法签名都不会匹配,因此将调用默认的对象运算符==方法.
- 在类上重载运算符时,二进制运算符的至少一个参数必须是包含类型,否则会生成编译器错误(错误BC33021 http://msdn.microsoft.com/en-us/library/watt39ff.aspx)
- 无法在接口上指定实现
请参阅下面演示问题的代码和输出.
问题
How do you provide proper operator overloads for your classes when using interface-base programming?个
参考文献
对于预定义的值类型,如果相等运算符(==)的操作数的值相等,则返回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 GetHash代码()
{
string composite = StreetName + City + State;
return composite.GetHash代码();
}
#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.