以前の出版物では、オブジェクトの値に基づいて比較を実装する最も完全で正しい方法を開発しました。これは、 .NETプラットフォームのクラスインスタンス( 参照型—参照型 )です。
オブジェクトの値- 構造のインスタンス( 「値による型」-値の型 )のインスタンスによる比較の正しい実装のために、提案された方法をどのように変更する必要がありますか?
構造インスタンスは、その性質上、常に価値が比較されます。
BooleanやInt32などの定義済みの型の場合、値の比較は、構造体インスタンスの値を直接比較することを意味します。
構造が開発者(プラットフォームのユーザー(ユーザー定義の構造))によって定義されている場合、デフォルトの比較は、構造インスタンスのフィールド値の比較として自動的に実装されます。 (詳細については、 ValueType.Equals(Object)メソッドの説明と==および!=演算子を参照してください)。 また、 ValueType.GetHashCode( )メソッドは、自動的に決定される方法でObject.GetHashCode()メソッドをオーバーライドします。
この場合、いくつかの重大な落とし穴があります。
フィールド値を比較するとき、反射が使用され、パフォーマンスに影響します。
構造フィールドは「重要」ではないかもしれませんが、 参照タイプであり、この場合、参照によるフィールドの比較はサブジェクト(ドメイン)の観点からは適切でない場合があり、値でフィールドを比較する必要がある場合があります(ただし、一般的な場合、構造内の参照フィールド誤ったアーキテクチャソリューションと見なされます)。
( ドキュメントでは、このような構造に対して、値による比較の独自の実装を作成して、生産性を高め、このタイプの平等の値を最も正確に反映することをお勧めします。)
客観的な観点から、すべてのフィールドが比較に参加する必要があるわけではないことが判明する場合があります(ただし、一般的な場合の構造については、これは誤ったソリューションと見なすことができます)。
- 最後に、 ValueType.GetHashCode()メソッドのデフォルトの実装は、GetHashCode()メソッドの実装に関する一般的な要件を満たしていません( 最初の出版物で説明しました )。
- ValueType.GetHashCode()を使用して取得したハッシュコード値は、ハッシュテーブルのキーとしての使用に適さない場合があります。
- オブジェクトの1つまたは複数のフィールドの値が変更された場合、 ValueType.GetHashCode()を使用して取得した値は、ハッシュテーブルのキーを使用するのにも適さない可能性があります。
- ドキュメントでは、GetHashCode()メソッドの独自の実装を作成することをお勧めします。このメソッドは、このタイプのハッシュコードの概念を最も正確に反映しています。
したがって、一方で、構造をプッシュして、値(パフォーマンス、ドメインモデルの一致)によってオブジェクトを比較する独自のメカニズムを実装する一般的な性質のいくつかの理由があります。
一方、GetHashCode()メソッドの正しい実装の必要性は、値による比較を実装する必要性に自動的につながります。 ハッシュコードの性質( 最初の出版物を参照)によるGetHashCode()メソッドは、値による比較にどのデータ(フィールド)およびそれらがどのように関与するかを「知る」必要があります。
第三に、例えば、構造フィールドのみで構成される「単純な」構造があり、リフレクションの助けを借りてのバイト比較が意味的に正しい結果(例えばInt32 )を明らかにする特別な場合があります。
この場合、値による比較の独自の実装を作成せずに、GetHashCode()を正しい方法で実装することができます(等しいオブジェクトの場合、ハッシュコードは常に同じです)。
例:
public struct Point { private int x; private int y; public int X { get { return x; } set { x = value; } } public int Y { get { return y; } set { y = value; } } public override int GetHashCode() => x.GetHashCode() ^ y.GetHashCode(); }
ただし、 「自動実装プロパティ」を使用してこの簡単な例を書き換える場合、画像はあまり明確に見えません。
public struct Point { public int X { get; set; } public int Y { get; set; } public override int GetHashCode() => X.GetHashCode() ^ Y.GetHashCode(); }
「自動プロパティ」のドキュメントは、パブリックプロパティに対応する匿名バッキングフィールドの自動作成を参照しています。
厳密に言えば、値による比較のデフォルト実装の観点から、XとYのペアワイズが同じ値を持つ2つのPointオブジェクトかどうかは、説明から明らかではありません。
- デフォルトの実装がフィールドの値とリフレクションを比較する場合、異なるオブジェクトの匿名フィールドはどのように比較されますか?これらのフィールドは互いに対応しているためです それぞれがプロパティXに対応し、これらは互いに対応しています。 それぞれがyに対応しますか?
2つの異なるオブジェクトが(x1、y1)および(x2、y2)という形式の異なる名前でバッキングフィールドを作成するとどうなりますか?
x1がx2に対応し、y1がy2に対応することを比較するときに考慮されますか?
- インターフェイスの観点から同じオブジェクトに対して異なる値を持つことができる追加の補助フィールドがありますか(X、Y)? その場合、比較する際にこれらのフィールドは考慮されますか?
- または、おそらく、 autoプロパティを持つ構造の場合、個々のフィールドを比較せずに、構造の内容全体のバイト比較が使用されますか? その場合、各オブジェクトのバッキングフィールドは常に同じ順序で同じオフセットでメモリに作成されますか?
おそらく、プラットフォームの開発に関連するドキュメントや著者の本のどこかで、これらの質問に対する肯定的な答えを見つけることができます-明示的に宣言されたフィールドを持つ構造と自動プロパティを持つ構造の動作はデフォルトの比較で同じ結果をもたらすという意味で値によって。
または、一部が文書化されていない場合は、コンパイラとランタイムの動作が予想される可能性が最も高いです。
引数の一般的なセットを考えると、一般的なケースでは、構造が値の独自の比較を実装することが望ましいようです。
前の出版物からおなじみのPerson実体に基づく詳細なコメントがある詳細な例は次の出版物で考慮されるでしょう。