Chapter.15

構造体に関する注意点

2005/1/7
 ・インターフェースが継承可能であることが判明したのでサンプルソースを修正。

1.構造体ポインタ

VB.NETで構造体をStructureとして定義した場合、現在のプラットフォーム相互運用機能では、その構造体のポインタを取る引数に、NULLを渡すことが出来ません。 VB.NETでは、構造体は値型System.ValueTypeの派生クラス)として扱われます。 値型は参照型とは異なり、参照を保持しているわけではないので、その内容(インスタンスと言うのが適当かも・・・)が存在していない状況はありえません。 それはつまり、C/C++のNULLポインタのような、参照先が不在にはならないと言うことです。

どうしても、プラットフォーム相互運用機能を使用して呼び出す関数にNULLポインタを渡したい場合、その引数を数値型IntegerまたはSystem.IntPtrとして宣言し、単純に0を渡してやれば可能です。 さらに、構造体を渡したい場合に備えて、その関数をオーバーロードしてしまえば完璧です。 下に例を示します。

Declare Function InvalidateRect Lib "USER32.DLL" ( _
    ByVal hWnd As System.IntPtr, _
    <[In]()> _
    ByRef lpRect As RECT, _
    <MarshalAs(UnmanagedType.Bool)> _
    ByVal bErase As Boolean _
) As <MarshalAs(UnmanagedType.Bool)> Boolean

Declare Function InvalidateRect Lib "USER32.DLL" ( _
    ByVal hWnd As System.IntPtr, _
    ByVal lpRect As System.IntPtr, _
    <MarshalAs(UnmanagedType.Bool)> _
    ByVal bErase As Boolean _
) As <MarshalAs(UnmanagedType.Bool)> Boolean

Declare Function InvalidateRect Lib "USER32.DLL" ( _
    ByVal hWnd As System.IntPtr, _
    ByVal lpRect As Integer, _
    <MarshalAs(UnmanagedType.Bool)> _
    ByVal bErase As Boolean _
) As <MarshalAs(UnmanagedType.Bool)> Boolean


2.COMインターフェースにおける構造体ポインタ

前項で述べた通り、オーバーロードを使用すれば、NULLポインタによる呼び出しと構造体による呼び出しを共存させることが可能です。 しかし、COMインターフェースのメソッドを定義する場合は、オーバーロードを使用することは出来ません。 なぜなら、COMインターフェースのV-Tableレイアウトを狂わせることになるからです。 そのため、NULLポインタを渡す可能性がある引数は、すべてIntPtrとして宣言するのも、その解決策の一つです。 その場合、Structureとして定義した構造体を使用しようとすると、System.Runtime.InteropServices.Marshalクラス内のPtrToStructureStructureToPtr及びDestroyStructureといったメソッドを使用することになります。 これらメソッドは適切に使用しなければ、マネッジドコードであるにも関わらず、メモリリークを発生させる原因となってしまいます。

VB.NETにおいて、リークを発生しにくいプログラムとは、マネッジドメモリのみを使用するプログラムです。 NULLポインタを渡すことが出来るならば、わざわざアンマネッジドメモリを使用する必要はありません。 最も単純にNULLポインタを渡すためには、構造体をStructureではなくClassとして定義すれば可能となります。 Classで宣言された変数・引数は、参照型となり明示的に割り当てなければ、インスタンスが設定されることはありません。 つまり、Nothingを渡してやれば、NULLポインタとしてマーシャリングされるのです。 ただし、同一の構造体のイコールによる単純な値のコピーが不可能になり、インスタンスの管理を適切に行う必要が出てきます。 これは、リークの回避とソースの可読性向上に比較して、それほどのデメリットとはならないと思います。

そこで構造体を全てClassとして定義したサンプルソースです。
VB.NETサンプルソース:chap15.vb.lzh(38KB 2005/1/7)


3.まとめ

構造体を引数に取るメソッドにNULLポインタを渡す可能性がある場合、その構造体はクラスとして定義した方が扱いが楽と言えます。 ただし、構造体クラスの欠点として、クラスの継承とインターフェースの実装を行うことが出来ません。 構造体に基本クラスを適用するとは考えにくいですが・・・。

実際のところ、現時点ではそれほど問題となっていません。 ただし、今後、構造体のポインタ変数がNULLかどうかによって、処理が大きく変わる部分を実装する可能性が高いため、今のうちに対策を立てておくことにしました。


前へ 次へ
OLE on .NET Frameworkへ
総合トップへ