- PR -

どんな型でも受け付けて比較できる方法はありますか?

投稿者投稿内容
がらす
ベテラン
会議室デビュー日: 2005/07/14
投稿数: 99
投稿日時: 2006-08-02 05:02
やってみました。少々名前変わってます、すみません。
最後のToString()は絶対もっといい方法があると思うのですが…。

コード:
public static bool operator ==(ID id1, ID id2)
        {            
            // TypeID is integer.
            if (id1.TypeID != id2.TypeID)
            {
                return false;
            }
            // SubmissionID can have any type. Type disagreement causes false.
            else if(id1.SubmissionID.GetType() != id2.SubmissionID.GetType())
            {
                return false;
            }
            // If it is not a value type, simply compare references.
            else if (id1.SubmissionID.GetType().IsClass)
            {
                return id1.SubmissionID == id2.SubmissionID;
            }
            // If it is value type, do value comparison.
            else
            {
                return id1.SubmissionID.ToString() == id2.SubmissionID.ToString();
            }
        }



いかがでしょう?
がらす
ベテラン
会議室デビュー日: 2005/07/14
投稿数: 99
投稿日時: 2006-08-02 05:20
べるさん、アドバイス有り難うございます。

引用:

べるさんの書き込み (2006-08-02 04:03) より:

囚人さんがおっしゃっている『「同値」をみたり「リファレンス」を見たりは駄目でしょう。』が、解決されていませんよ。



「こういうときは値、こういうときはリファレンス」と定義した場合でも駄目なのでしょうか?
囚人
ぬし
会議室デビュー日: 2005/08/13
投稿数: 1019
投稿日時: 2006-08-02 12:16
「クラスのインスタンスが等しい」という意味を持たせられるのはクラスの設計者だけです。
あるクラスはリファレンスが等しい事を等しいとするし、また別のクラスは内部の ID が等しければ等しいとします。

Uri クラスを例に挙げましょう。
コード:
Uri u1 = new Uri( "http://www.atmarkit.co.jp" );
Uri u2 = new Uri( "http://www.atmarkit.co.jp" );

Console.WriteLine( u1 == u2 ); // 1.x では false、2.0 では true
Console.WriteLine( u1.Equals( u2 ) ); // true
Console.WriteLine( ReferenceEquals( u1, u2 ) ); // false


u1 と u2 は別のインスタンスですからリファレンス比較では当然別物です。
しかし、Uri クラスの設計者は同一の URI を表している場合は「等しい」という意味としたい、と考えているわけです。

がらすさんの考えでは、そういったクラス設計者の考えを殺してしまいます。

「インスタンスの等しさ」を決定付けたクラス設計者はハッシュコードも生成方法も当然決定します。
つまり「同値なら同じハッシュコードを生成する」としているわけです。

がらすさんの SubmissionID は「等しくないのに同じハッシュコードを生成する」場合が当然あります。
これが問題になるかならないかは以後の実装に依りますが、爆弾を抱えている事には違いないでしょう。

ちなみに、等値演算子がリファレンス比較をするという前提も間違いです。
がらすさんがご自身で既に行っているように等値演算子はオーバーロードできます。
Uri クラスは 1.x の時は設計がまずくて、Equals メソッドをオーバーライドしているのに、等値演算子をオーバーロードしていませんでした。
このような事が起こり得るので、リファレンス比較をしたい場合は、上記で挙げたように必ず ReferenceEquals メソッドを使う方がよいでしょう。

ということで、
引用:

クライエントコードを書く人が出来るだけ自由に使える方法を提供しようとしています。


がらすさんの設計では逆に縛りが強くなると私は考えますが、いかがでしょうか?

_________________
囚人のジレンマな日々
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2006-08-02 21:59
引用:

仕様がダメだということなのですが、こんな風に変えてみるとどうでしょう?
2つのclientIDの比較で
- 2つの型が違う場合は false
- 値型が渡された場合、値を比べて、一致するかどうかで true/false
- 値型でない場合はリファレンス比較


 string クラス。これ、参照型です。参照型なので、リファレンス比較になりますね。いいの?


私なら、ClientIDBase を定義して、それを継承したクラスだけにするかなぁ。。。
コード:
public abstract class ClientIdBase : IComparable
{
	public abstract object Id {
		get;
		set;
	}
}


 これなら、「同じ型同士比較が出来て、object 型の出し入れ可能なプロパティを持ったクラス」となります。

 「自由」って言葉を間違って理解している人が多いようです。「自由」と「制約がない」は違います。「制約がない」ことを「自由」といっている人が多いようです。
 例えば、我々は、「法律」という制約の元に自由です。法律によって、他人の生命財産に危害を及ぼしてはいけない代わりに、他人からも危害が及ぼされないことが保証されているのです。

 なんの制約もない自由よりも、一定の制約の下での自由の方が、行動しやすいのですよ?
がらす
ベテラン
会議室デビュー日: 2005/07/14
投稿数: 99
投稿日時: 2006-08-04 03:19
囚人さん、Jittaさん、どうも有り難うございます。
stringが足りなかったと言って判定ルーチンに足せば、今度はUriが足りないのに気づき…という困ったことになりそうですね。

そこでどういう仕様にするかを考えているわけですが、SubmissionID の型をobjectから何か比較方法を限定できるインタフェースかベースクラスにしようと思っています。intにしてしまえば楽でよい気もするのですが。

それから、細かい点で疑問に思ったことをいくつか質問させてください。
引用:

囚人さん:
ちなみに、等値演算子がリファレンス比較をするという前提も間違いです。



とのことですが、SubmissionID はobject型なので
id1.SubmissionID==id2.SubmissionID
はリファレンス比較になると思っていました。この考え方は間違っていますか?

それから、Jittaさんの提案されている
コード:

public abstract class ClientIdBase : IComparable
{
public abstract object Id {
get;
set;
}
}



このクラスから継承しても Id は object なので、どんな型でも渡せます。これでは、結局「比較する時にどうやって比較するかが分からない」という元の問題に戻ってしまいませんか?私が最初に書いた
コード:

public class ID
{
public int guid;
public object clientID;
}


で、clientID メンバが object であることが問題の原因だと思っていた(たとえば、intだったら何も悩むことは無い)のですが、どうなのでしょう?


[ メッセージ編集済み 編集者: がらす 編集日時 2006-08-04 06:13 ]
囚人
ぬし
会議室デビュー日: 2005/08/13
投稿数: 1019
投稿日時: 2006-08-04 12:48
引用:

とのことですが、SubmissionID はobject型なので
id1.SubmissionID==id2.SubmissionID
はリファレンス比較になると思っていました。この考え方は間違っていますか?


いえ、正しいです。
ただリファレンス比較を必ずしたい、というのを明示するには ReferenseEquals がよいです。

で、結局、Equals メソッドを使うのは何故駄目なんですか?
_________________
囚人のジレンマな日々
がらす
ベテラン
会議室デビュー日: 2005/07/14
投稿数: 99
投稿日時: 2006-08-05 06:30
囚人さん、そのほかの皆さん、どうも有り難うございます。

引用:

囚人さんの書き込み (2006-08-04 12:48) より:

で、結局、Equals メソッドを使うのは何故駄目なんですか?



すみません、私が間違えていたようです。 Equals と == の違いは null チェックだけだと思っていました。== でobject{int}同士の比較がリファレンス比較になってしまったので、Equalsでも同じだと思っていました。
Equals と == で、object{int}同士を比較したときの結果が異なることを知りませんでした。Equals で値比較をしているようなので、Equals でやります。

しかし、未だに何故違いがあるのか(何故そんな実装になっているのか)理由が分かりません。ちょっと実験してみたのですが、コメントに「?」がついている行について、どなたか動作の理由(設計思想)を教えていただけませんでしょうか?

コード:

// Object{object} (予想:リファレンス比較で全部false←正解)
object o = new object();
object o2 = new object();
a = o.Equals(o2); //false
a = Equals(o, o2); //false
a = o == o2; //false
a = ReferenceEquals(o, o2);//false

Object{int} (予想:リファレンス比較で全部false←ハズレ)
o = 123;
o2 = 123;
a = o.Equals(o2); //true ← intとして比較。思ったより賢い
a = Equals(o, o2); //true ← intとして比較
a = o == o2; //false ← objectとしてリファレンス比較?何故???
a = ReferenceEquals(o, o2);//false ← 当然リファレンス比較

Object{double}(予想:リファレンス比較で全部false←ハズレ)
o = 123.5d;
o2 = 123.5d;
a = o.Equals(o2); //true ← doubleとして比較
a = Equals(o, o2); //true ← doubleとして比較
a = o == o2; //false ← objectとしてリファレンス比較?
a = ReferenceEquals(o, o2);//false ← 当然リファレンス比較

Object{int} vs. Object{string} (予想:型違いで当然false←正解)
o = 123;
o2 = "123";
a = o.Equals(o2); //false
a = Equals(o, o2); //false
a = o == o2; //false
a = ReferenceEquals(o,o2); //false

Object{string}(予想:リファレンス比較で全部false←ハズレ)
o = "123";
o2 = "123";
a = o.Equals(o2); //true ← stringとして比較
a = Equals(o, o2); //true ← stringとして比較
a = o == o2; //true ← stringとして比較
a = ReferenceEquals(o, o2);//true ← 別インスタンスじゃ無いのか??
メモリ節約のため同じ文字列で別メモリ確保は行わないってことか??



どうぞよろしくお願いします。

[ メッセージ編集済み 編集者: がらす 編集日時 2006-08-05 06:36 ]
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2006-08-05 09:15
なぜ Equals の動作が違うかと言えば、そりゃもちろんオーバーライドしてるからです。
派生クラスが基底クラスのメソッドをオーバーライドしたら、基底クラス型に派生クラスインスタンスを入れてメソッドを呼び出しても、実際に呼び出されるのはオーバーライドした派生クラスのメソッドでしょう?

引用:

Object{int} (予想:リファレンス比較で全部false←ハズレ)
o = 123;
o2 = 123;
a = o == o2; //false ← objectとしてリファレンス比較?何故???


== は演算子です。演算子はオーバーライドできません。ですから左右の インスタンスではなく型次第で 結果が変わります。
両者は object 型ですから、参照の比較になります。

引用:

Object{double}(予想:リファレンス比較で全部false←ハズレ)
o = 123.5d;
o2 = 123.5d;
a = o == o2; //false ← objectとしてリファレンス比較?


同上。

引用:

Object{int} vs. Object{string} (予想:型違いで当然false←正解)


型が違うというより、オブジェクトが違うと言った方が良いような気がします。

引用:

Object{string}(予想:リファレンス比較で全部false←ハズレ)
o = "123";
o2 = "123";
a = ReferenceEquals(o, o2);//true ← 別インスタンスじゃ無いのか??
メモリ節約のため同じ文字列で別メモリ確保は行わないってことか??


文字列リテラルにはインターンプールという仕組みが働きます。要は同じ文字列は同じインスタンスを使うって事ですが。
コンストラクタを使って作成した String インスタンスの場合は普通の参照比較になります。

スキルアップ/キャリアアップ(JOB@IT)