C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[8]: 参照型の等値演算子でboxingを回避したい


(過去ログ 54 を表示中)

[トピック内 9 記事 (1 - 9 表示)]  << 0 >>

■30605 / inTopicNo.1)  参照型の等値演算子でboxingを回避したい
  
□投稿者/ ほにゃ (1回)-(2008/12/27(Sat) 22:15:35)

分類:[C#] 

こんばんは。
以下のようなコードがあります。

interface IHoge { }
class HogeClass { }
static class Test
{
    public static bool Test1(HogeClass x, HogeClass y) { return x == y; }
    public static bool Test2(IHoge x, IHoge y) { return x == y; }
    public static bool Test3<T>(T x, T y) where T : HogeClass { return x == y; }
    public static bool Test4<T>(T x, T y) where T : class, IHoge { return x == y; }
}

上記のコードをビルドしてMSILコードを確認すると

Test1 boxingは発生しない
Test2 boxingは発生しない
Test3 boxingが発生する
Test4 boxingが発生する

となるようなのです。
Test3やTest4でboxingが発生するのはなぜなのでしょうか?
Test3やTest4の場合でboxingを回避する方法はないでしょうか?



引用返信 編集キー/
■30606 / inTopicNo.2)  Re[1]: 参照型の等値演算子でboxingを回避したい
□投稿者/ NyaRuRu (79回)-(2008/12/27(Sat) 22:27:19)
No30605 (ほにゃ さん) に返信
> 上記のコードをビルドしてMSILコードを確認すると
>
> Test1 boxingは発生しない
> Test2 boxingは発生しない
> Test3 boxingが発生する
> Test4 boxingが発生する
>
> となるようなのです。
> Test3やTest4でboxingが発生するのはなぜなのでしょうか?
> Test3やTest4の場合でboxingを回避する方法はないでしょうか?

そもそも値型が出てきていないようですが,どのインスタンスがボックス化されるのでしょうか?
引用返信 編集キー/
■30607 / inTopicNo.3)  Re[2]: 参照型の等値演算子でboxingを回避したい
□投稿者/ ほにゃ (2回)-(2008/12/27(Sat) 22:35:48)
No30606 (NyaRuRu さん) に返信
> そもそも値型が出てきていないようですが,どのインスタンスがボックス化されるのでしょうか? 

たぶん、x と y の両方がボックス化されるのかと・・・
NyaRuRuさんのBoxingCheckerだと、以下のように出力されます。
これはboxingではないのでしょうか?

BoxingTest.Test.Test3
    IL_0000     nop
    IL_0001     ldarg.0
    IL_0002     box
    IL_0007     ldarg.1
    IL_0008     box
    IL_000D     ceq
    IL_000F     stloc.0
    IL_0010     br.s
    IL_0012     ldloc.0
    IL_0013     ret

BoxingTest.Test.Test4
    IL_0000     nop
    IL_0001     ldarg.0
    IL_0002     box
    IL_0007     ldarg.1
    IL_0008     box
    IL_000D     ceq
    IL_000F     stloc.0
    IL_0010     br.s
    IL_0012     ldloc.0
    IL_0013     ret

引用返信 編集キー/
■30609 / inTopicNo.4)  Re[3]: 参照型の等値演算子でboxingを回避したい
□投稿者/ ほにゃ (3回)-(2008/12/27(Sat) 22:40:18)
ildasm.exeだとこんな感じです。

  .method public hidebysig static bool  Test3<(BoxingTest.HogeClass) T>(!!T x,
                                                                        !!T y) cil managed
  {
    // コード サイズ       20 (0x14)
    .maxstack  2
    .locals init ([0] bool CS$1$0000)
//000021:         public static bool Test3<T>(T x, T y) where T : HogeClass { return x == y; }
    IL_0000:  nop
    IL_0001:  ldarg.0
    IL_0002:  box        !!T
    IL_0007:  ldarg.1
    IL_0008:  box        !!T
    IL_000d:  ceq
    IL_000f:  stloc.0
    IL_0010:  br.s       IL_0012

    IL_0012:  ldloc.0
    IL_0013:  ret
  } // end of method Test::Test3

  .method public hidebysig static bool  Test4<class (BoxingTest.IHoge) T>(!!T x,
                                                                          !!T y) cil managed
  {
    // コード サイズ       20 (0x14)
    .maxstack  2
    .locals init ([0] bool CS$1$0000)
//000022:         public static bool Test4<T>(T x, T y) where T : class, IHoge { return x == y; }
    IL_0000:  nop
    IL_0001:  ldarg.0
    IL_0002:  box        !!T
    IL_0007:  ldarg.1
    IL_0008:  box        !!T
    IL_000d:  ceq
    IL_000f:  stloc.0
    IL_0010:  br.s       IL_0012

    IL_0012:  ldloc.0
    IL_0013:  ret
  } // end of method Test::Test4



IL_0002:  box        !!T   ← x
IL_0008:  box        !!T      ← y
ではないかと、想像してるのですが。

引用返信 編集キー/
■30610 / inTopicNo.5)  Re[4]: 参照型の等値演算子でboxingを回避したい
□投稿者/ NyaRuRu (80回)-(2008/12/27(Sat) 22:51:54)
2008/12/27(Sat) 22:58:25 編集(投稿者)
2008/12/27(Sat) 22:52:21 編集(投稿者)

No30609 (ほにゃ さん) に返信
> IL_0002: box !!T   ← x
> IL_0008: box !!T ← y
> ではないかと、想像してるのですが。

Tが値型のときは確かにこの部分でボックス化が発生します.
Tが参照型のときは,JIT コンパイル時に無視されます.何もおきません.

http://standards.iso.org/ittf/PubliclyAvailableStandards/c042927_ISO_IEC_23271_2006(E).zip
ISO/IEC 23271:2006 Partition III p.103
>If typeTok is a reference type, the box instruction does nothing.

今回の場合はGeneric Methodの制約のため実行時にTが値型となることはありえないはずです.
よって,IL には box 命令が存在したとしても,実行時にボックス化が起こることはありません.

念のため注意しておいて欲しいのですが,ボックス化という概念を必要とするのは値型のみです.
参照型に対しては,そもそもボックス化という概念が必要がありません.
よって,値型が一切出てこないコードでボックス化を気にしているのであれば,どこかに勘違いがあるように思います.
引用返信 編集キー/
■30613 / inTopicNo.6)  Re[5]: 参照型の等値演算子でboxingを回避したい
□投稿者/ ほにゃ (4回)-(2008/12/27(Sat) 23:09:44)
No30610 (NyaRuRu さん) に返信
> 今回の場合はGeneric Methodの制約のため実行時にTが値型となることはありえないはずです.
> よって,IL には box 命令が存在したとしても,実行時にボックス化が起こることはありません.
>
> 念のため注意しておいて欲しいのですが,ボックス化という概念を必要とするのは値型のみです.
> 参照型に対しては,そもそもボックス化という概念が必要がありません.
> よって,値型が一切出てこないコードでボックス化を気にしているのであれば,どこかに勘違いがあるように思います.

参照型であれば、box命令があっても気にする必要はないのですね。
「なんで参照型なのにボックス化されるんだろう?」
と悩んでいました。
ありがとうございます。

解決済み
引用返信 編集キー/
■30615 / inTopicNo.7)  Re[6]: 参照型の等値演算子でboxingを回避したい
□投稿者/ NyaRuRu (81回)-(2008/12/27(Sat) 23:37:16)
2008/12/27(Sat) 23:58:45 編集(投稿者)
(追記:
同値性:equivalence, 等値性:equality なので,
BCL のスタンスはあくまで"等値性"の再定義ということですね.
C# の言語仕様も「参照型等値演算子」と.
http://msdn.microsoft.com/ja-jp/library/aa664727.aspx

私の誤解で失礼しました.
本投稿は無視して頂けると幸いです.
)

■No30613 (ほにゃ さん) に返信
> 参照型であれば、box命令があっても気にする必要はないのですね。
> 「なんで参照型なのにボックス化されるんだろう?」
> と悩んでいました。
> ありがとうございます。

念のため一点だけ確認したいのですが
タイトルには「参照型の等値演算子」とある一方で,
上に挙げられたコードの少なくとも Test2〜Test4 では
いわゆる参照の同一性を評価しているのが微妙に気になります.
(Test1 は == 演算子のオーバーロード次第)

例えば,以下のように HogeClass の Equals をオーバーライドして,
何と比較されても常に等しいと返すようにしたとします.

interface IHoge { }
class HogeClass : IHoge
{
    public override bool Equals(object obj) { return true; }
    public override int GetHashCode() { return 0; }
}

にもかかわらず,以下のコードは両方 False を返します.

Console.WriteLine(Test.Test3(new HogeClass(), new HogeClass()));
Console.WriteLine(Test.Test4(new HogeClass(), new HogeClass()));

これは,Test3/Test4 の IL が,常に参照の同一性を比較しようとする
ものだからなのですが,本当にこれは意図した動作ですか?
意図通りであれば,(等値演算子という表現は別として)
こちらの心配のしすぎでしたので読み飛ばしてください.

参考) http://d.hatena.ne.jp/siokoshou/20070527#p3

いわゆる同値性の再定義 (等値性をとるか同一性をとるか) に対応するのであれば,
以下のように IEquatable<T> を使うのがセオリーかなと.

static class Test2
{
    public static bool EqTest1<T>(T x, T y) where T : IHoge, IEquatable<T>
    { return x != null ? x.Equals(y) : y == null; }
    public static bool EqTest2<T>(T x, T y) where T : IHoge, IEquatable<T>
    { return EqualityComparer<T>.Default.Equals(x, y); }
}

解決済み
引用返信 編集キー/
■30617 / inTopicNo.8)  Re[7]: 参照型の等値演算子でboxingを回避したい
□投稿者/ なちゃ (242回)-(2008/12/28(Sun) 00:02:20)
No30615 (NyaRuRu さん) に返信
> 以下のように IEquatable<T> を使うのがセオリーかなと.
>
> static class Test2
> {
> public static bool EqTest1<T>(T x, T y) where T : IHoge, IEquatable<T>
> { return x != null ? x.Equals(y) : y == null; }
> public static bool EqTest2<T>(T x, T y) where T : IHoge, IEquatable<T>
> { return EqualityComparer<T>.Default.Equals(x, y); }
> }

んーむしろEqualityComparer<T>.Default.Equalsだけのがいい気が
引用返信 編集キー/
■30623 / inTopicNo.9)  Re[8]: 参照型の等値演算子でboxingを回避したい
□投稿者/ NyaRuRu (82回)-(2008/12/28(Sun) 01:30:08)
2008/12/28(Sun) 01:41:16 編集(投稿者)

No30617 (なちゃ さん) に返信
> ■No30615 (NyaRuRu さん) に返信
>>以下のように IEquatable<T> を使うのがセオリーかなと.
>>
>>static class Test2
>>{
>> public static bool EqTest1<T>(T x, T y) where T : IHoge, IEquatable<T>
>> { return x != null ? x.Equals(y) : y == null; }
>> public static bool EqTest2<T>(T x, T y) where T : IHoge, IEquatable<T>
>> { return EqualityComparer<T>.Default.Equals(x, y); }
>>}
>
> んーむしろEqualityComparer<T>.Default.Equalsだけのがいい気が

確かに IEquatable<T> を要請するべきかどうか含めてケースバイケースかもしれませんね.

ドキュメントや公開されているソースコードから動作やパフォーマンスの違いは判断できると思うので,
どれをどう使うかの判断は利用者の方におまかせします.
http://msdn.microsoft.com/ja-jp/library/ms224763.aspx
http://referencesource.microsoft.com/netframework.aspx

# EqualityComparer<T>.Default の実装の気合の入り方は,よくここまでやるなと思います.
# T = Nullable<U> でかつ U が IEquatable<U> を実装している場合への対応や,
# T = byte のときを特別扱いした高速化等
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -