超不定期雑記

~プログラムとかサイエンスとかいろいろと~

目次

Blog 利用状況

ニュース

書庫

ジェネリッククラス内では==に制限がある?

VisualStudio2005 の C# で "==" を定義したクラスを作ったんですが、それをジェネリッククラス内で使おうとしたら呼び出されませんでした。

裸で使えば問題なく動作するんですが、ジェネリック変数同士では Object.operator== が呼び出されているように思えます。

サンプルコード

class Program
{
    static void Main( string[] args )
    {
        SampleData data0 = new SampleData();
        SampleData data1 = new SampleData();
        bool ret_operator = ( data0 == data1 );

        GenericData<SampleData> gen_data = new GenericData<SampleData>();
        gen_data.data1 = new SampleData();
        gen_data.data2 = new SampleData();
        gen_data.OperatorEq();
    }
}

class GenericData<TYPE> where TYPE : class
{
    public TYPE data1 = null;
    public TYPE data2 = null;

    public void OperatorEq()
    {
        Console.WriteLine( "GenericData.OperatorEq Start" );

        bool ret_operator = ( data1 == data2 );

        Console.WriteLine( "GenericData.OperatorEq End" );
    }
}

class SampleData
{
    public static bool operator ==( SampleData lhs, SampleData rhs )
    {
        Console.WriteLine( "SampleData.operator==" );

        return true;
    }

    public static bool operator !=( SampleData lhs, SampleData rhs )
    {
        Console.WriteLine( "SampleData.operator!=" );

        return !( lhs == rhs );
    }

    public override int GetHashCode()
    {
        return base.GetHashCode();
    }

    public override bool Equals( object obj )
    {
        return base.Equals( obj );
    }
}

結果

SampleData.operator==
GenericData.OperatorEq Start
GenericData.OperatorEq End

投稿日時 : 2008年7月8日 17:28

Feedback

# re: ジェネリッククラス内では==に制限がある? 2008/07/09 0:10 Streetw☆

型が特に限定されてないジェネリックで呼び出せるのは
object型のものだけなので、
==はobject型のもの(参照の比較)が呼び出されるんだと思います。

bool ret_operator = data1.Equals(data2);
だと、overrideされてるEqualsが呼び出されますよ。

# re: ジェネリッククラス内では==に制限がある? 2008/07/09 9:41 guicheng

コメントありがとうございます。

この後調べてみましたら、ジェネリック型変数をジェネリッククラス外で使えば想定通りに動くようです。

先の例ですと Main 関数で
bool ret_operator = ( gen_data.data1 == gen_data.data2 );
とすると、SampleData クラスのものが呼び出されます。

# re: ジェネリッククラス内では==に制限がある? 2008/07/09 10:46 Streetw☆

私の文章、少し変更します~

「型が特に限定されてないジェネリッククラス内では、
型パラメータによって指定された型のオブジェクトに対して
呼び出せるのは、
object型によって宣言されるメンバに限られるので、・・・」

ばっちり書いてる訳じゃなかったので昨日は書かなかったんですけど、
上の文章は「C# Language Specification 2.0.doc」の「19.1.4 制約」
のものを少し変更したものです。

Main関数(ジェネリッククラスの外側)ではSampleDataのもので、
ジェネリッククラス内ではobjectのものになるのは、
そういう仕様なんだって思います。
Equalsだと大丈夫なのは、==はオーバーロードだけど、
Equalsはオーバーライドされてるからだって思います。

# re: ジェネリッククラス内では==に制限がある? 2008/07/09 11:34 guicheng

なるほど、この動作で仕様通りだったんですね。

そうすると、operator== の実装は
return (lhs==null) ? (rhs==null) : lhs.Equals(rhs);
として、Equals の中に比較の実体を書くということになりますか。

またジェネリッククラスのメンバ関数では、== を使わずに Equals で比較するといった工夫も必要ですね。

# re: ジェネリッククラス内では==に制限がある? 2008/07/09 12:02 guicheng

スミマセン、間違えました。
operator== の実装で == なんか使ったら、無限ループしてしまいます。
正しくは以下の通りです。

Object.ReferenceEquals( lhs, null ) ?
Object.ReferenceEquals( rhs, null ) :
lhs.Equals( rhs );

# re: ジェネリッククラス内では==に制限がある? 2008/07/09 13:40 Streetw☆

> この動作で仕様通りだったんですね

でも、わんくまの方、だれもコメントされなかったから
自信はちょっとないです;

> 無限ループしてしまいます

私よくしちゃいますww
これでも大丈夫です。
return ((object)lhs==null) ? ((object)rhs==null) : lhs.Equals(rhs);

> そうすると、operator== の実装は

最近、私、operator==の実装とかしてたんですが、
↓のってスッキリしてるかも!って思いました。

class SampleData
{
 public override bool Equals(object obj)
 {
  ・・・
 }
 public static bool Equals(SampleData lhs, SampleData rhs)
 {
  // nullの判断とかoverrideの呼び出しとかしてくれます~
  return object.Equals(lhs, rhs);
 }
 public static bool operator ==(SampleData lhs, SampleData rhs)
 {
  return Equals(lhs, rhs);
 }
 public static bool operator !=(SampleData lhs, SampleData rhs)
 {
  return !Equals(lhs, rhs);
 }
}
lhsとrhsって、lとrは左右だと思うけど、hsって何の略ですか?

# re: ジェネリッククラス内では==に制限がある? 2008/07/09 16:21 guicheng

> これでも大丈夫です。

なるほど、キャストすれば簡単に書けますね。
気がつかなかった……


> スッキリしてるかも!って思いました。

この書き方は目から鱗です。
object.Equals が override も考慮して呼び出してくれるとは知りませんでした。


> lhsとrhsって、lとrは左右だと思うけど、hsって何の略ですか?

Left/Right Hand Side の略で、二項演算子の左辺値・右辺値の意味で使っています。

# re: ジェネリッククラス内では==に制限がある? 2008/07/09 16:56 Streetw☆

object o = new SampleData();
o.Equals(null);
が、SampleDataのEqualsが呼び出されるのと同じことです。
って、私、ちょ~~しにのってます♪w

> 左辺値・右辺値の意味で使っています

へ~、それかっこいいです。
お箸持つ方の感覚ですね(違

# re: ジェネリッククラス内では==に制限がある? 2008/07/10 10:06 guicheng

> object o = new SampleData();
> o.Equals(null);
> が、SampleDataのEqualsが呼び出されるのと同じことです。

これはちょっと疑問が。
override の挙動を考える際に、普通のメンバ関数と静的関数を同列に扱っていいものでしょうか。
今回のケースでは同じ結果になるというのでしたら異論はありませんが。


> へ~、それかっこいいです。

もちろん私が考えたものではなく、本に載っていたものをまねているんですが。
Effective C++ だったかな?


ところで、二つ上のコメント(#148131)では static な Equals を定義していますが、これを定義せずに、operator== で直接 Object.Eqauls を呼び出してやるのはどうでしょうか。

タイトル
名前
Url
コメント