前回の【無駄話】AI vs. AI-私の勉強法(1) et. seq. の通り、DataGridViewの列ヘッダークリックによるソートの為に、Listのソートについて学ぶために、クラスオブジェクトの同等判定比較判定について復習(と言うか、学習...)しましたので、その結果を備忘しておきます。(

:Windows 11標準装備の.NET Framework 4.8のC# 5.0ではデリゲートの'=>'、Nullableの'?'や'is null'演算子が使えませんので、それらは使っていません。

 

クラスオブジェクトを宣言、定義した場合、既定となる同等判定

(1)Equalsメソッド

(2)ReferenceEqualsメソッド及び

(3)'=='演算子

となりますが、これらは全て参照比較)となり、「完全に同じインスタンス」でないと全て偽(false)になります。

このため、(アドレス比較を目的としたReferenceEqualsメソッド以外の)Equalsメソッドと'=='演算子について、「クラスメンバーの値が同等の場合、同等とする」仕様であれば、ユーザーがそのように実装する必要があります。

:クラスという設計図に基づき、メモリーに作られた実体<インスタンス>のメモリーアドレスを比較すること。

 

次にクラスオブジェクトの比較判定を行うには、クラス宣言の際に": ICompare<(クラス名)>"を付加すれば、既定CompareToメソッドが使われます。但しC#の仕様から比較対象要素は一つとなるので、複数の異なる要素で比較しようとする場合、それぞれ別のメソッドを設ける必要があります。(そしてその場合谷津のCompareToメソッドを使う訳ではないので、クラス宣言に必ずしも": ICompare<(クラス名)>"を付加する必要はありません。)

 

サンプルのコンソールプログラムを組んでみましたので、ご参考までに以下に掲示します。

 

【Equals_Comparison.cs】

///////////////////////////////////
// Test program for Class Objects'
// Equals Method and
// Comparison Method
///////////////////////////////////

using System;

namespace Equals_Comparison
{
    public class TestObject_Org : IComparable<TestObject_Org>
    {
        public string Name {get; set;}
        public int Val {get; set;}
    /*
    「既定のObjectの同等比較」
    比較方法            既定の内部動作                Equals を修正した場合の挙動
    --------------------------------------
    ReferenceEquals        常にメモリーアドレスを比較    変更不可(常に参照比較)
    Equals メソッド        既定は参照比較(同上)        値比較への変更可能
    == 演算子            既定は参照比較                自動では変わらない
    <参考-C# 7.0以降>
    'is null'演算子        ReferenceEquals(対象Obj, null)と等価
    ("== null"は等価ではない)
    */


        //比較メソッドの定義
        public int CompareTo(TestObject_Org other)
        {
            if(ReferenceEquals(other, null))        //null参照比較
                return 1;                            //thisが大きい
            return this.Val.CompareTo(other.Val);    //Valで比較:0なら等しい、負なら小さい、正なら大きい
        }

        public static bool operator <(TestObject_Org left, TestObject_Org right)
        {
            //leftがnullの場合
            if(ReferenceEquals(left, null))
                return !ReferenceEquals(right, null);    //rightがnullでなければ「左 < 右」はtrue、両方nullなら false
            //leftが nullでなければ、内蔵のCompareToに判定を委ねる
            return left.CompareTo(right) < 0;
        }

        public static bool operator >(TestObject_Org left, TestObject_Org right)
        {
            //leftがnullの場合は絶対に「右より大きい」にはならない
            if(ReferenceEquals(left, null))
                return false;
            //leftが nullでなければ、内蔵のCompareToに判定を委ねる
            return left.CompareTo(right) > 0;
        }

        public static bool operator <=(TestObject_Org left, TestObject_Org right)
        {
            return ReferenceEquals(left, null) || left.CompareTo(right) <= 0;
        }

        public static bool operator >=(TestObject_Org left, TestObject_Org right)
        {
            if (ReferenceEquals(left, null))
                return ReferenceEquals(right, null); //両方nullのときだけtrue
            return left.CompareTo(right) >= 0;
        }
    }

    public class TestObject_Rev
    {
        public string Name {get; set;}
        public int Val {get; set;}

        //Equalsメソッドに値比較を追加
        public override bool Equals(object obj)
        {
            if(ReferenceEquals(obj, null))                //null参照比較
                return false;
            if(obj.GetType() != typeof(TestObject_Rev))    //型相違
                return false;
            return (this.Name == ((TestObject_Rev)obj).Name &&
                    this.Val == ((TestObject_Rev)obj).Val);
        }

        //GetHashCodeメソッドの拡張(EqualsのNameとValの比較の為に必要)
        public override int GetHashCode()
        {
            //null参照による例外を防ぐためのチェック(intは初期値'0'が入る)
            int hashName = (this.Name != null) ? this.Name.GetHashCode() : 0;
            int hashVal = this.Val.GetHashCode();
            //2つのハッシュ値を掛け算と足し算で合成
            //397(素数)を乗じ、値の偏り(衝突)を抑制
            unchecked
            {
                int hash = 17;
                hash = (hash * 397) ^ hashName;
                hash = (hash * 397) ^ hashVal;
                return hash;
            }
        }

        //演算子'=='の定義
        public static bool operator ==(TestObject_Rev left, TestObject_Rev right)
        {
            if(ReferenceEquals(left, right))        //同一オブジェクト
                return true;
            else
                if(ReferenceEquals(left, null) || ReferenceEquals(right, null))    //何れか(または両方)がnull
                    return false;
            return left.Equals(right);                //それ以外の場合は比較
        }

        //演算子'!='の定義
        public static bool operator !=(TestObject_Rev left, TestObject_Rev right)
        {
            return !(left == right);                //単なる'=='の否定
        }

        //比較メソッドの定義
        public int CompareNameTo(TestObject_Rev other)
        {
            if(ReferenceEquals(other, null))        //null参照比較
                return 1;                            //thisが大きい
            return this.Name.CompareTo(other.Name);    //0なら等しい、負なら小さい、正なら大きい
        }

        public int CompareValTo(TestObject_Rev other)
        {
            if(ReferenceEquals(other, null))        //null参照比較
                return 1;                            //thisが大きい
            return this.Val.CompareTo(other.Val);    //0なら等しい、負なら小さい、正なら大きい
        }

        public static void Main()
        {
            //Testing TesatObject_Org as to if two are same
            TestObject_Org test1 = new TestObject_Org() {Name = "Ysama", Val = 1};
            TestObject_Org test2 = new TestObject_Org() {Name = "Ysama", Val = 1};
            Console.WriteLine("TestObject_Org test1 : Name = \"Ysama\", Val = 1");
            Console.WriteLine("TestObject_Org test2 : Name = \"Ysama\", Val = 1");
            Console.WriteLine("TestObject_Org test1 == test2 : {0}", test1 == test2);
            Console.WriteLine("TestObject_Org test1.Equals(test2) : {0}", test1.Equals(test2));
            Console.WriteLine("TestObject_Org ReferenceEquals(test1, test2) : {0}", ReferenceEquals(test1, test2));
            Console.WriteLine(">>> Following are comparison result focusing on Val. <<<");
            Console.WriteLine("TestObject_Org test1.CompareTo(test2) : {0}", test1.CompareTo(test2));
            Console.WriteLine("TestObject_Org test1 < test2 : {0}", test1 < test2);
            Console.WriteLine("TestObject_Org test1 > test2 : {0}", test1 > test2);
            Console.WriteLine("TestObject_Org test1 <= test2 : {0}", test1 <= test2);
            Console.WriteLine("TestObject_Org test1 >= test2 : {0}", test1 >= test2);
            Console.WriteLine();
            //Testing TesatObject_Rev as to if two are same and which is bigger/smaller
            TestObject_Rev test3 = new TestObject_Rev() {Name = "Ysama", Val = 1};
            TestObject_Rev test4 = new TestObject_Rev() {Name = "Ysama", Val = 1};
            Console.WriteLine("TestObject_Rev test3 : Name = \"Ysama\", Val = 1");
            Console.WriteLine("TestObject_Rev test4 : Name = \"Ysama\", Val = 1");
            Console.WriteLine("TestObject_Rev test3 == test4 : {0}", test3 == test4);
            Console.WriteLine("TestObject_Rev test3.Equals(test4) : {0}", test3.Equals(test4));
            Console.WriteLine("TestObject_Rev ReferenceEquals(test3, test4) : {0}", ReferenceEquals(test3, test4));
            Console.WriteLine("TestObject_Rev test3.CompareNameTo(test4) : {0}", test3.CompareNameTo(test4));
            Console.WriteLine("TestObject_Rev test3.CompareValTo(test4) : {0}", test3.CompareValTo(test4));
            //Wait
            Console.Write("Type any key ...");
            Console.Read();
        }
    }
}

 

さて、ここまで理解したのでどのようにDataGridViewに臨むかの最終方針を決めないとなりませんね。

 

ps. DataGridViewについてはChat-GPT、クラスオブジェクトの同等、比較判定の一般論についてはGoogle Gemini に確認し、Chat-GPTのアド倍酢についてGeminiに意見を求めました。(【無駄話】AI vs. AI-私の勉強法(3)

いつも腰の低いAI」のGeminiの躾として、他の競合AIの批判や誹謗は行わない、ということのようです。

 

人間よりも優れていますね!?

 

ps. of ps. (道徳、倫理のような価値観による制限のない)AIは「最終目的尾の為には手段を択ばない」ので、学習の結果、不正行為を行うことは必定。「いつまで続くのかな」という目で見ておきましょう。