その1では、ネイティブリソースをイミュータブルなクラスでラップする方法を示します。イミュータブルとは、インスタンスの内部状態が、変化しないクラスのことです。ナイーブな実装は↓のようなものです。
class A { protected IntPtr handle; public A() { handle = Api.Alloc(); } ~A() { Api.Release(handle); } public void Method() { Api.Method(handle); } }
Api
クラスでネイティブ API の呼びだしが実装されていることを想定した疑似コードです。実際には、Api.Alloc
では、ILCreateFromPathW
でアイテムIDリストを作成したり、Api.Release
では、ILFree
で開放したりします。
しかし、c# では、ファイナライザーの実行タイミング
がかなりアグレッシブなため、(環境によっては)、稀に不正なメモリーアクセスなどの原因で、クラッシュします。
Api.Method(handle)
実行中に、~A()
が呼ばれる場合があるためです。これを避けるには、↓のようにします。
class A { protected IntPtr handle; public A() { handle = Api.Alloc(); } ~A() { Api.Release(handle); } public void Method() { try { Api.Method(handle); } finally { GC.KeepAlive(this); } } }
handle を使う場合には、handle の必要がなくなる場所で、
GC.KeepAlive(this)
を呼びだして、早期の開放を阻止します。
handle
はスレッド間で共有されるので一般的には、ロックが必要ですが、handle
をコンストラクターとファイナライザー以外で変更しなければロックは不要だと思います。(イミュータブルなクラスを採用したのはこのためです)
handle
を外部で使用しない方が安全ですが、公開する場合には、やはり、GC.KeepAlive
で早期開放を阻止する必要があります。
ミュータブルなクラスでは、lock
の必要があるので、ネイティブリソースをラップする方法 (その2)
のように保護すると良いでしょう。
このサイトのページへのリンクは自由に行っていただいてかまいません。
このサイトで公開している全ての画像、プログラム、文書の無断転載を禁止します。
ここをクリック
すると表示されるページから作者へメールで連絡できます。