Garbage Collection

塵も積もれば山

目次

Blog 利用状況

ニュース

コミケで受けていた通販をすべて発送しました。詳しくはこちらの記事にて
C++とかC#とか数学ネタを投下していく予定です。
それ以外の日々の四方山話を綴った日記はこちら

書庫

日記カテゴリ

[C#]スーパー等しい君

Equalsメソッドの実装のお話です。

合同式を作ってみました。
今回の場合は、13を法とした合同式です。
以下の場合、1と14はどちらも13で割った余りが1になるので、等しいとなるはずです。

class Mod13 {
  uint num;
  public override bool Equals(object obj) {
    var right = obj as Mod13;
    if (right == null) return base.Equals(obj);
    return getMod() == right.getMod();   
  }
  public uint getMod() { return num % 13; }
  public Mod13(uint num) {
    this.num = num;
  }
}
class Program {
  static void Main(string[] args) {
    var a = new Mod13(1);
    var b = new Mod13(14);
    Console.WriteLine("1 ≡ 14 (mod 13) :" + (a == b));
  }
}

はい、この通り1と14が等しいことに…なってないですね。
Equalsをちゃんとオーバーライドしているのに…おかしいなぁ。

答え
Equals と operator== は別物です。

ということで、operator== もちゃんと追加しました。
なぜか、operator!= も作れとコンパイラに怒られたのでそちらも作っています。
これでちゃんと動くはずです。

class Mod13 {
  static public bool operator ==(Mod13 left, Mod13 right) {
    return left.Equals(right);
  }
  static public bool operator !=(Mod13 left, Mod13 right) {
    return !(left == right);
  }
}

ぬ、ぬるぽーーーー!?
Equalsの中でoperator== を使っているのが原因らしいです。

答え
operator== を先に作り、Equals がoperator== を利用するようにしましょう。

#ObjectがEqualsを持つ必要性がよくわかんない。IHashable のインターフェースでは駄目なの!?

投稿日時 : 2008年10月6日 23:02

Feedback

# re: [C#]スーパー等しい君 2008/10/06 23:31 れい

> operator== を先に作り、Equals がoperator== を利用するようにしましょう。

これには異議を唱えたいです。

速度が問題にならないなら…
staticでEqualsを実装し、インスタンスメンバのEqualsと==はstatic Equalsを参照するようにしたい。
ObjectはEqualsを必ず持つが==は持たない場合もあるので、Equalsが==に依存すると==だけ削除したい時に困る。

>#ObjectがEqualsを持つ必要性がよくわかんない。IHashable のインターフェースでは駄目なの!?

IHashableは欲しいと思いますが、それはGetHashCodeの方が主ですよね。
Equalsの主な役割はListなどのコレクションサポートですよね。
Contains/IndexOfしたいときとか。
まぁこれもIEquatableで良いと言えば良いはずです。
ToStringも同様で。

その辺は構造的美しさと実用性や歴史的事情との兼ね合いなので、考えてもしょうがないかなと。

GetHashCode、Equals、ToStringがObjectにあるのはちょうどいいくらいかなと思います。

コレクションにオブジェクトを突っ込むのにいちいちIHashableやIEquatableを実装するのはめんどくさいです。

# re: [C#]スーパー等しい君 2008/10/07 2:15 れい

読み直しててNullReference部分がすごく気になったので追記。

> static public bool operator ==(Mod13 right, Mod13 left) {

こっちのほうがいいですよね…
static public bool operator ==(Mod13 left, Mod13 right) {

前者だと
> if (left == null) return base.Equals(obj);
ここでrightがnullになっちゃってNullReferenceなんですよね。
普通後者なのでStackOverFlowですね。

> Equalsの中でoperator== を使っているのが原因らしいです。
nullに対して==を使うのが原因ですね。
Object.ReferenceEqualsを使わないと。

私なら、Equalsや==、!=を実装する時は下記のようにします。
私はついでにIEquatable<>も実装しますが、ここでは省略。
Compareや<、<=等も大体こんな感じに作ります。

static Boolean Equals(Mod13 a, Mod13 b) {
if (Object.ReferenceEquals(a, b)) return true;
if (Object.ReferenceEquals(a, null)) return false;
if (Object.ReferenceEquals(b, null)) return false;
//ここで等しいか判定
return a.getMod() == b.getMod();
}

public override bool Equals(object obj) { return Mod13.Equals(this, obj); }
static public bool operator ==(Mod13 left, Mod13 right) { return Mod13.Equals(left, right); }
static public bool operator !=(Mod13 left, Mod13 right) { return !Mod13.Equals(left, right); }

# re: [C#]スーパー等しい君 2008/10/07 7:48 出水

operator==があるんだから、これを差し置いてEqualsがある理由がわからないんですよね

で、Equalsだけ実装するとGetHashCodeも作れ!って言われるんで
こいつらはセットでHash専用と考えるのが正しい気がします

そもそもListやLinkedListがEqualsを要求するのがおかしくて
operator==を使うか、ラムダ式等で検索時に与えてやるべきではないかと思います

>ObjectはEqualsを必ず持つが==は持たない場合もある
operator== を持たない? 参照の比較として必ず持っているんではないですか?

>static public bool operator ==(Mod13 left, Mod13 right)
ぎゃーす、移すときに間違えた!(直しておきます)
でも、こっちでもStackOverFlowじゃなくてNullReferenceなんですよね

# re: [C#]スーパー等しい君 2008/10/07 8:31 れい

> operator==があるんだから、これを差し置いてEqualsがある理由がわからないんですよね

operator==を上書きできない言語もありましたし(昔のVB)、==を使わない言語も可能なので、operator無しでも全部できるように作る必要があったからですよ。

> で、Equalsだけ実装するとGetHashCodeも作れ!って言われるんで

HashCodeが違うのにEqualsがtrueなObjectがあったらHashに入らなくなっちゃいます。
なので、Equalsを実装するならGetHashCodeを要請するのは当然でしょう。
なので、EqualsがHash用と考えるのは違います。
もちろんHashにも使いますが。

> そもそもListやLinkedListがEqualsを要求するのがおかしくて
> operator==を使うか、ラムダ式等で検索時に与えてやるべきではないかと思います

過去の上に載ってますから、そもそも論をしてもしょうがないかと。
1.0とか1.1にはラムダ式ないし。

Equater見たいなクラスとか、デリゲートを毎回渡さなきゃいけないCollectionを用意するのは可能だったでしょうが、それは面倒すぎます。

> でも、こっちでもStackOverFlowじゃなくてNullReferenceなんですよね

だからそれは
if (right == null) return base.Equals(obj);
ここが悪いんですって。

タイトル  
名前  
Url
コメント