システムを通過するさまざまな文字列 ID 用に、厳密に型指定された不変のラッパー クラスを作成しました。
抽象 BaseId クラス:
(簡潔にするために一部のエラーチェックとフォーマットを省略しています...)
public abstract class BaseId
{
// Gets the type name of the derived (concrete) class
protected abstract string TypeName { get; }
protected internal string Id { get; private set; }
protected BaseId(string id) { Id = id; }
// Called by T.Equals(T) where T is a derived type
protected bool Equals(BaseId other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
return String.Equals(Id, other.Id);
}
// warning CS0660 (see comment #1 below)
//public override bool Equals(object obj) { return base.Equals(obj); }
public override int GetHashCode()
{
return TypeName.GetHashCode() * 17 + Id.GetHashCode();
}
public override string ToString()
{
return TypeName + ":" + Id;
}
// All T1 == T2 comparisons come here (where T1 and T2 are one
// or more derived types)
public static bool operator ==(BaseId left, BaseId right)
{
// Eventually calls left.Equals(object right), which is
// overridden in the derived class
return Equals(left, right);
}
public static bool operator !=(BaseId left, BaseId right)
{
// Eventually calls left.Equals(object right), which is
// overridden in the derived class
return !Equals(left, right);
}
}
私の目標は、派生クラスが小さくなり、大部分/全体が定型コードで構成されるように、実装の多くを基本クラスに保持することでした。
具体的な DerivedId クラスの例:
この派生型は、独自の追加の状態を定義しないことに注意してください。その目的は、強い型を作成することだけです。
public sealed class DerivedId : BaseId, IEquatable<DerivedId>
{
protected override string TypeName { get { return "DerivedId"; } }
public DerivedId(string id) : base(id) {}
public bool Equals(DerivedId other)
{
// Method signature ensures same (or derived) types, so
// defer to BaseId.Equals(object) override
return base.Equals(other);
}
// Override this so that unrelated derived types (e.g. BarId)
// NEVER match, regardless of underlying Id string value
public override bool Equals(object obj)
{
// Pass obj or null for non-DerivedId types to our
// Equals(DerivedId) override
return Equals(obj as DerivedId);
}
// warning CS0659 (see comment #2 below)
//public override int GetHashCode() { return base.GetHashCode(); }
}
各クラスはコンパイラ警告を生成しています:
BaseId で Object.Equals(object o) をオーバーライドしないと、コンパイル警告が生成されます。
warning CS0660: 'BaseId' defines operator == or operator != but does not override Object.Equals(object o)
ただし、BaseId.Equals(object o) を実装すると、Object.Equals(object o) で基本クラスの実装を呼び出すだけになります。とにかく、これがどのように呼び出されるかはわかりません。派生クラスでは常にオーバーライドされ、そこでの実装はこの実装を呼び出しません。DerivedId で BaseId.GetHashCode() をオーバーライドしないと、コンパイル警告が生成されます。
warning CS0659: 'DerivedId' overrides Object.Equals(object o) but does not override Object.GetHashCode()
この派生クラスには追加の状態がないため、BaseId.GetHashCode で基本クラスの実装を呼び出すことを除いて、DerivedId.GetHashCode() の実装で行うことはありません。 ().
コンパイラの警告を抑制するか、メソッドを実装して基本クラスの実装を呼び出させることができますが、何かが欠けていないことを確認したいと考えています。
私がこれを行った方法に奇妙なことがありますか、それとも正しいコードの警告を抑制するためにこれを行う必要があることの 1 つにすぎませんか?