バックグラウンド
現在のプロジェクトでインターフェイス ベースのプログラミングを使用していますが、演算子 (具体的には等値演算子と不等値演算子) をオーバーロードするときに問題が発生しました。
仮定
- C# 3.0、.NET 3.5、および Visual Studio 2008 を使用しています
更新 - 次の仮定は誤りでした!
- すべての比較で operator== ではなく Equals を使用することを要求することは、特に型をライブラリ (コレクションなど) に渡す場合には、実行可能な解決策ではありません。
operator== ではなく Equals を使用する必要があることを懸念していた理由は、.NET ガイドラインのどこにも、operator== ではなく Equals を使用すると述べているか、またはそれを提案することさえなかったからです。ただし、 Equals と Operator== をオーバーライドするためのガイドラインを読み直した後、次のことがわかりました。
デフォルトでは、演算子 == は、2 つの参照が同じオブジェクトを示しているかどうかを判断することにより、参照が等しいかどうかをテストします。したがって、参照型は、この機能を得るために operator == を実装する必要はありません。型が不変の場合、つまり、インスタンスに含まれるデータを変更できない場合、演算子 == をオーバーロードして、参照の等価性ではなく値の等価性を比較すると便利です。これは、不変オブジェクトとして long と同じと見なすことができるためです。値が同じだからです。不変でない型で operator == をオーバーライドすることはお勧めできません。
およびこの等価インターフェイス
IEquatable インターフェイスは、Contains、IndexOf、LastIndexOf、Remove などのメソッドで等しいかどうかをテストするときに、Dictionary、List、LinkedList などの汎用コレクション オブジェクトによって使用されます。ジェネリック コレクションに格納される可能性のあるすべてのオブジェクトに対して実装する必要があります。
制約
- どのソリューションでも、オブジェクトをインターフェイスから具象型にキャストする必要があってはなりません。
問題
- operator== の両側がインターフェースである場合、基礎となる具象型の operator== オーバーロード メソッド シグネチャが一致しないため、デフォルトの Object operator== メソッドが呼び出されます。
- クラスで演算子をオーバーロードする場合、二項演算子のパラメーターの少なくとも 1 つが含まれている型である必要があります。そうでない場合、コンパイラ エラーが生成されます (エラー BC33021 http://msdn.microsoft.com/en-us/library/watt39ff .aspx )
- インターフェイスで実装を指定することはできません
問題を示す以下のコードと出力を参照してください。
質問
インターフェイスベースのプログラミングを使用する場合、クラスに適切な演算子のオーバーロードをどのように提供しますか?
参考文献
定義済みの値の型の場合、等価演算子 (==) は、オペランドの値が等しい場合は true を返し、そうでない場合は false を返します。文字列以外の参照型の場合、2 つのオペランドが同じオブジェクトを参照している場合、== は 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.