C#(と言っても私の場合、Windows 11に標準添付の.NET FrameworkのC# 5.0)DataGridViewの使い方について理解を深めていますが()、

:私の場合、プログラミングをしていると「何を作るか」よりも、「何故こうなるのか?」「どうしてそうなのか?」の答えを求めることが重要になってきてしまう癖があります。

 

(1)DataGridViewでは内部にデータを持たせないで、外部ソースの参照で処理する。

(2)それはソートの場合でも同じで、外部ソースをListにするとListを先ずソートし、そのコピーを外部ソースとして与えることでDataGridViewをリフレッシュする(らしい-まだ実験していません。)

 

ということまで分かりましたが、ここにきて

 

「クラスオブジェクトのソートには、先ずオブジェクトの比較判定が必要」

 

という単純な事実に直面し、私がその問題にあまり詳しくなかったことが分かって

 

ガーン!!!

 

と打ちのめされました。

 

ということで、

 

(読者に隠れて)基礎中の基礎である

 

「クラスオブジェクトの宣言において、どのように同等判定や比較判定を行うのか?」

 

を再学習しています。

 

そんなこんなで、サンプルプログラムを書き乍らこの問題を、(DataGridViewではChat-GPTに訊いていましたが)Google Geminiに質問してみました。(セカンドオピニオンですかね?)そのやり取りの抜粋をご紹介します。

 

【私とGeminiとの「ちょっと恥ずかしい基礎的講座」】

Q:C# の自作クラスオブジェクトを作り、等号(==)条件で調べるときの、既定の条件(例:すべてのメソッドフィールドやプロパティの値が等しい)は何でしょうか?

 

A:C#で自作クラス(class)のオブジェクトを等号(==)で比較する場合、既定(デフォルト)の挙動は「参照の比較(同一性)」となります。すべてのフィールドやプロパティの値が等しいかどうか(同値性)を判定するわけではありません。

 

 

1. 既定の挙動(参照比較)

  • 仕組み: 2つの変数がメモリ上の同じ実体(インスタンス)を指しているかを判定します。
  • 結果: たとえば、すべてのプロパティ値が完全に同じインスタンスを2つ作っても、== の結果は false になります。 
var obj1 = new MyClass { Id = 1, Name = "Test" };
var obj2 = new MyClass { Id = 1, Name = "Test" };
bool result = (obj1 == obj2); // 結果は false (インスタンスの場所が違うため)

2. 「値の比較」に変える3つの方法

(本題に関係が乏しいので省略)

 

Q:単にObjectの内部仕様を確認しています。 例えば自作クラスに値比較のEqualsメソッドを作ると、自動的にエイリアスが働き、等号(==)でも値比較になりますか?それとも等号は独立してReferenceEqualsの働きしかしませんか?

 

 A:結論から言うと、 Equalsメソッドを実装しただけでは「参照比較」のままです。等号式(==)は独立して動くため、自動的にエイリアスが働くことはありません。オブジェクトの内部仕様として、Equals メソッドと == 演算子は完全に切り離されています。

 

1. なぜ連動しないのか?

C#(および .NET)のコンパイラから見ると、これらは全く異なる命令として処理されるためです。

  • Equals メソッド: インスタンスの仮想メソッド(またはインターフェースの実装)として呼び出されます。
  • == 演算子: コンパイル時に静的なメソッド(静的バインディング)として解決されます。

そのため、クラス(class)にどれだけ精巧な Equals メソッドを定義しても、== を使った瞬間にコンパイラは既定の「参照比較(ポインタの比較)」のILコードを出力します。

 

2. .NET 内部での具体的な動作

一般的な参照型(class)における、それぞれの内部的な役割は以下の通りです。

 

比較方法 既定の内部動作 Equals を修正した場合の挙動
ReferenceEquals 常にメモリ上の参照(ポインタ)を比較する 変更不可(常に参照比較)
Equals メソッド 既定は参照比較(ReferenceEquals と同じ) 値比較に変更できる
== 演算子 既定は参照比較(内部で型による最適化あり) 自動では変わらない(参照比較のまま)

 

3. == でも値比較にしたい場合の内部仕様

等号(==)でも値比較をさせたい場合は、明示的に 演算子オーバーロード(operator == をクラスに記述する必要があり、この中でEqualsメソッドを呼び出す、という形を取るのがC#の標準的な実装パターンです。

このように、開発者が手動で「== の中で Equals を呼ぶ」というパイプを繋いであげることで、初めて等号でも値比較ができるようになります。

 

Q:サンプルのコードをコンパイルしたら error CS0216: 演算子 'Equals_Comparison.TestObject.operator ==(Equals_Comparison.TestObject?, Equals_Comparison.TestObject?)' を定義するには、合致する 演算子 '!=' が必要です。 というエラーが出ました。 又、Comparisonメソッド(これはObjectに汎用ですか?)についても同等に考えてよいですか?

 

A:エラーが発生してしまい失礼いたしました。メッセージの通り、C#の言語仕様として == を定義(オーバーロード)する場合は、対になる != も必ず同時に定義しなければならないという厳格なルールがあります。先ほどのサンプルコードで != の記述が漏れていたため、コンパイルエラー(CS0216)が発生しました。

後半のご質問である「Comparison メソッド(および関連する比較機能)」についても、== と同様に自動で連動するかどうか、オブジェクトの内部仕様を解説します。

 

 1. 「Comparison」に関する内部仕様と結論

結論から言うと、 CompareTo(あるいは Comparison)と、==Equals などの大小比較機能は完全に独立しています。どれか1つを実装しても、他の機能へ自動的にエイリアス(連動)されることはありません。

C#におけるオブジェクトの比較は、内部的に「同値性(同じか)」「順序性(どちらが大きいか)」の2つの系統に完全に分離されています。

 

① Objectクラスに「Comparison」メソッドはある?

正確な仕様として、System.Object(すべての基底クラス)には Comparison というメソッドは存在しません。 Object が持つ既定の比較メソッドは EqualsReferenceEquals のみです。

「Comparison」という言葉が指すものには、主に以下の2つがあります。

  • IComparable インターフェース(CompareTo メソッド): クラスに「大小比較」の能力を持たせるための標準仕様。
  • Comparison<T> デリゲート: ソート(並び替え)のルールを外部から渡すためのメソッドの型。

② 大小比較(><)と Equals の関係

もし自作クラスに「大小を比較する CompareTo」を実装しても、自動的に ==Equals がその結果(戻り値が0なら等しい、など)を利用することはありません。

 

(サンプルコードでエラーが多発したので...多くは省略)

 

A:ご不便をおかけしてしまい、重ねて申し訳ありません。原因を正確に突き止めましたので解説します。

このエラーと警告の原因は、C#の「Null許容参照型」の有効化状態と、記述のミスマッチによるものです。

 

字数制限の為、(2)に続く。