System.Type.GUID プロパティと GuidAttribute

ref: Type.GUID プロパティ (System)
ref: GuidAttribute クラス (System.Runtime.InteropServices)

まとめ

  • GuidAttribute を型に付けることで、Type.GUID の値を指定できる
    • GuidAttribute を付けておけば、コンパイラやランタイムに依存せずに Type.Guid を取得できる
  • GuidAttribute が無い場合の Type.GUID の値は、ランタイムに依存
    • .net framework なら、型の完全限定名を元に GUID を振ってくれる
    • mono の場合、GuidAttribute が存在しない限り、Type.GUID は Null Guid*1 になる
  • Type.GUID は、現実的には COM Interop

そもそも Type.GUID は何のためにあるのか

MSDN の Type.GUID の解説には「GUID は、GuidAttribute 属性を使用して型に関連付けられています。」とあるので GuidAttribute についての記述を見てみると、タイプライブラリ インポーターが使う旨が読み取れる。

このことから、Type.GUID は COM Interop のためにあると考えられる。

任意の型について、GUID はどのようにして決定されるのか

Generic 型について

Generic な型については、Type.GUID は型引数が与えられているかどうかに関わらず同じ値を返す。

つまり、

typeof(List<>).GUID
typeof(List<string>).GUID
typeof(List<int>).GUID

は全て同じ Guid を返す。

よって Generic な型もそうでない型も Type.Guid に関する振る舞いは同じなので、以下では区別せずに扱う。

GuidAttribute がある場合

Type.Guid{ get; } は、GuidAttribute で指定した GUID をそのまま返す。

これについては、csc.exe、gmcs のどちらでコンパイルしても、.net framework、mono のどちらで実行しても正しく動作する。

GuidAttribute がない場合

GuidAttribute がない場合には、Type.GUID の値はランタイムで決定される。つまり、アセンブリを作ったコンパイラではなく、実行している環境に依存した値になる。

具体的には、

  • .net framework の場合: 型の完全限定名*2を元にした値
  • mono の場合: Null Guid

が得られる。

なお、前者については、GuidAttribute の無い適当な型を作って、型の完全限定名を変えながら実行してみると分かるが、たとえクラスの中身が別物だろうと完全限定名が一致すれば同じ Type.GUID になる。

また、後者については、Re: (Mono-dev) Type.GUID patch の議論にあるとおり、

といった理由があるようだ。

Type.GUID は COM 相互運用のための値

個人的に Type.GUID が気になった理由は、自作シリアライザなどで、型の完全限定名を毎回付加するのがサイズ的に大きくて悲しいことへの対処に使おうと考えたからであった。

のだが、この状況を見るに、Type.GUID は、「Type に対応する GUID 値」でなく、(おそらく MS の人が意図しているであろう通り、) COM 相互運用のための値と割り切ったほうが無難なようだ。

*1:全ビットが 0 の GUID

*2:アセンブリ名やバージョンも含む