public override bool Equals(object obj)
{
// 1. null 檢查
if (obj is null) return false;
// 2. 參考相等(同一物件)
if (ReferenceEquals(this, obj)) return true;
// 3. 型別完全相等(不可用 is/as,避免衍生類型通過)
if (obj.GetType() != GetType()) return false;
// 4. 內容比較
return Equals((MyType)obj);
}
protected virtual bool Equals(MyType other)
{
return _field1 == other._field1 && _field2 == other._field2;
}
// 實作 IEquatable
public bool Equals(MyType other) => Equals((object)other);
public class Base
{
public override bool Equals(object obj)
{
if (obj is null) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false; // 關鍵:型別完全相等
return Equals((Base)obj);
}
}
public class Derived : Base
{
public override bool Equals(object obj)
{
if (obj is null) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return base.Equals(obj) && Equals((Derived)obj);
}
}
關鍵:GetType() != GetType() 確保對稱性。若用 is Base,base.Equals(derived) 可能回傳 true,但 derived.Equals(base) 回傳 false → 破壞對稱性。
| 機制 | 用途 | 是否覆寫 |
|---|---|---|
Object.ReferenceEquals | 參考相等(同一實例) | 靜態,不可覆寫,永遠正確 |
Object.Equals(static) | 靜態輔助,處理 null | 靜態,不可覆寫,永遠正確 |
Object.Equals(instance) | 值相等(內容相等) | 實值型別必覆寫,參考型別視語意覆寫 |
operator == | 值相等語法糖 | 實值型別必覆寫,參考型別通常不覆寫(保留參考語意) |
override bool Equals(object)bool Equals(T)(實作 IEquatable)operator == / operator !=並必須覆寫 GetHashCode()(見原則 10)。
只有當「值相等」語意不同於「參考相等」時才覆寫(如 string、Uri、DTO)。大多數參考型別保留預設參考相等。
ReferenceEquals、static EqualsGetType() 檢查確保對稱性IEquatable 避免裝箱