私はC#プロジェクトに取り組んでいます。そのプロジェクトでは、これまで、不変オブジェクトとファクトリを使用して、タイプFoo
のオブジェクトが常に==
と等しいかどうかを比較できるようにしています。
Foo
オブジェクトは一度作成すると変更できません。ファクトリは常に、指定された引数のセットに対して同じオブジェクトを返します。これはうまく機能し、コードベース全体で、==
が常に等しいかどうかのチェックに機能すると想定しています。
次に、これが常に機能するとは限らないEdgeケースを導入するいくつかの機能を追加する必要があります。最も簡単なことは、そのタイプのoperator ==
をオーバーロードして、プロジェクト内の他のコードを変更する必要がないようにすることです。しかし、これはコードのにおいとして私を襲います:Equals
ではなくoperator ==
をオーバーロードすることは奇妙に思えるだけで、==
が参照の等価性をチェックするという慣例に慣れており、Equals
はオブジェクトの等価性(または用語が何であれ)をチェックします。
これは正当な懸念事項ですか、それともoperator ==
に過負荷をかけるべきですか?
標準では、ほとんどのタイプで、.Equalsがオブジェクトの類似性をチェックし、演算子==
が参照の等価性をチェックするというのが標準だと思います。
ベストプラクティスは、不変型の場合、演算子==
は.Equals
と同様に類似性をチェックする必要があるということです。そして、それらが本当に同じオブジェクトであるかどうかを知りたい場合は、.ReferenceEquals
を使用します。この例については、C#String
クラスを参照してください。
overloading==
とoverriding Equalsの間には大きな違いがあります。
あなたが表現を持っているとき
if (x == y) {
変数xとyを比較するために使用される方法は、コンパイル時に決定されます。これは演算子のオーバーロードです。 xとyを宣言するときに使用されるタイプは、それらを比較するために使用されるメソッドを定義するために使用されます。 xおよびy内の実際のタイプ(つまり、サブクラスまたはインターフェースの実装)は関係ありません。以下を検討してください。
object x = "hello";
object y = 'h' + "Ello"; // ensure it's a different reference
if (x == y) { // evaluates to FALSE
そして以下
string x = "hello";
string y = 'h' + "Ello"; // ensure it's a different reference
if (x == y) { // evaluates to TRUE
これは、変数xとyの宣言に使用される型が、==の評価に使用されるメソッドを決定するために使用されることを示しています。
比較すると、Equalsは、変数x内の実際の型に基づいてruntimeで決定されます。 Equalsは、他の型がオーバーライドできる、または実行できるObjectの仮想メソッドです。したがって、次の2つの例はどちらもtrueと評価されます。
object x = "hello";
object y = 'h' + "Ello"; // ensure it's a different reference
if (x.Equals(y)) { // evaluates to TRUE
そして以下
string x = "hello";
string y = 'h' + "Ello"; // ensure it's a different reference
if (x.Equals(y)) { // also evaluates to TRUE
それは間違いなくにおいがします。 _==
_をオーバーロードするときは、Equals()
とGetHashCode()
の両方にも一貫性があることを確認する必要があります。 MSDNガイドライン を参照してください。
そして、これがまったく問題ないと思われる唯一の理由は、型を不変として記述することです。
不変の型の場合、値の等価性をサポートするために_==
_をオーバーロードすることに問題はないと思います。同じセマンティクスを持つようにEquals
をオーバーライドせずに_==
_をオーバーライドすることはないと思います。 _==
_をオーバーライドし、何らかの理由で参照の等価性を確認する必要がある場合は、Object.ReferenceEquals(a,b)
を使用できます。
これを参照してください いくつかの有用なガイドラインについてはMicrosoftの記事
MSFTガイドライン (下記)に従ってこれを実装する方法を示すサンプル。 Equalsをオーバーライドする場合は、GetHashCode()もオーバーライドする必要があることに注意してください。これが人々の役に立つことを願っています。
public class Person
{
public Guid Id { get; private set; }
public Person(Guid id)
{
Id = id;
}
public Person()
{
Id = System.Guid.NewGuid();
}
public static bool operator ==(Person p1, Person p2)
{
bool rc;
if (System.Object.ReferenceEquals(p1, p2))
{
rc = true;
}
else if (((object)p1 == null) || ((object)p2 == null))
{
rc = false;
}
else
{
rc = (p1.Id.CompareTo(p2.Id) == 0);
}
return rc;
}
public static bool operator !=(Person p1, Person p2)
{
return !(p1 == p2);
}
public override bool Equals(object obj)
{
bool rc = false;
if (obj is Person)
{
Person p2 = obj as Person;
rc = (this == p2);
}
return rc;
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
}
マイクロソフト独自のベストプラクティスによると、Equalsメソッドの結果と等号(==)オーバーロードは同じになるはずです。