- PR -

インスタンスがスコープから外れる時に終了処理を確実に実行する方法

1
投稿者投稿内容
ひろし
ぬし
会議室デビュー日: 2002/09/16
投稿数: 390
お住まい・勤務地: 兵庫県
投稿日時: 2005-03-05 19:07
要件
(1) インスタンスがスコープから外れるタイミングで確実に終了処理を実行したい。
(2) 終了処理の実行を明示的に指示しなくて済むようにしたい。
更に
(3) 新たなコンストラクタによって再初期化される前の値(つまりまさに消滅しようとしているインスタンスが持っている情報)を使って終了処理をしたい。(Example3)
更に
(4) 常に終了処理が完了するのを待って新たなコンストラクタによる再初期化が開始されようにしたい。

確かに、要件(1)(2)だけであれば、Example1のようにTestクラスを定義をして、
Example2のように使用をすることで一応満足できますが、
要件(3)、つまりExample3のように記述できるほうがよりエレガントです。
ところが、Dispose()の中でコンストラクタで初期化したパラメータを使用しなくては
ならない場合、正常に処理することができません。
Dispose()はいつ実行されるか分からないためコンストラクタで再初期化した後の値が
参照される可能性があります。
更に
可能であるなら、要件(4)にあるような、厳しいタイミング制御もできればと思っています。
良い工夫(つまりTestクラスの記述方法)はないでしょうか?

// *** Exsample1 ***
public class Test : IDisposable
{
public Test(int p)
{
thisP = p;
// 初期化処理

}

public void Dispose()
{
// 終了処理
// コンストラクタで与えられた"thisP"を使って記述したい

}
private int thisP;
}

// *** Example2 ***

public class Main
{
public void Exec()
{
Test test;

test = new Test(123);

test.Dispose(); // Test(123)の終了処理
test = new Test(345);

test.Dispose(); // Test(345)の終了処理
test = new Test(567);

test.Dispose(); // Test(567)の終了処理
}
}

// *** Example3 ***

public class Main
{
public void Exec()
{
// *** 明示的に指示しなくてもインスタンスが廃棄されると
// 自動的にDispose()が機能するようにしたい ***
Test test;
test = new Test(345);

test = new Test(567);

test = new Test(678);

}
}

ひろし
ぬし
会議室デビュー日: 2002/09/16
投稿数: 390
お住まい・勤務地: 兵庫県
投稿日時: 2005-03-05 19:16
文書の記入にミスがありました。下記のように訂正します。

質問1 要件(1)(2)(3)全てを満たす方法を知りたい
質問2 要件(1)(2)(3)(4)全てを満たす方法を知りたい

要件
(1) インスタンスがスコープから外れるタイミングで確実に終了処理を実行したい。
更に
(2) 終了処理の実行を明示的に指示しなくて済むようにしたい。
(3) 新たなコンストラクタによって再初期化される前の値(つまりまさに消滅しようとしているインスタンスが持っている情報)を使って終了処理をしたい。(Example3)
更に
(4) 常に終了処理が完了するのを待って新たなコンストラクタによる再初期化が開始されようにしたい。

確かに、要件(1)だけであれば、Example1のようにTestクラスを定義をして、
Example2のように使用をすることで一応満足できますが、
要件(2)、つまりExample3のように記述できるほうがよりエレガントです。
ところが、要件(3)のようなケースでは、Dispose()の中でコンストラクタで初期化したパラメータを使用しなくてはならない場合、正常に処理することができません。
Dispose()はいつ実行されるか分からないためコンストラクタで再初期化した後の値が
参照される可能性があります。
更に
可能であるなら、要件(4)にあるような、厳しいタイミング制御もできればと思っています。
良い工夫(つまりTestクラスの記述方法)はないでしょうか?

// *** Exsample1 ***
public class Test : IDisposable
{
public Test(int p)
{
thisP = p;
// 初期化処理

}

public void Dispose()
{
// 終了処理
// コンストラクタで与えられた"thisP"を使って記述したい

}
private int thisP;
}

// *** Example2 ***

public class Main
{
public void Exec()
{
Test test;

test = new Test(123);

test.Dispose(); // Test(123)の終了処理
test = new Test(345);

test.Dispose(); // Test(345)の終了処理
test = new Test(567);

test.Dispose(); // Test(567)の終了処理
}
}

// *** Example3 ***

public class Main
{
public void Exec()
{
// *** 明示的に指示しなくてもインスタンスが廃棄されると
// 自動的にDispose()が機能するようにしたい ***
Test test;
test = new Test(345);

test = new Test(567);

test = new Test(678);

}
}

_________________
中博俊
ベテラン
会議室デビュー日: 2004/10/17
投稿数: 91
お住まい・勤務地: 大阪市
投稿日時: 2005-03-05 19:21
using
_________________
中博俊 MSMVP Visual Studio C# Since 2004/04-2005/03, MCP
http://naka.wankuma.com/
http://blogs.wankuma.com/naka/
naka@wankuma.com
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2005-03-05 19:30
こんにちは、じゃんぬねっと です。

引用:

using



ちなみに、VB.NET, J# なら、Finally。
こんな感じで。

http://jeanne.wankuma.com/tips/dialog/04-folderbrowser.html
_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
ひろし
ぬし
会議室デビュー日: 2002/09/16
投稿数: 390
お住まい・勤務地: 兵庫県
投稿日時: 2005-03-07 15:30
ご指摘のとおり、C# の using(object){…} で記述できました。
ありがとうございました。
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2005-03-07 18:46
> 終了処理の実行を明示的に指示しなくて済むようにしたい。
 無理です。いつ終了させたいのかは、開発者しか知らないからです。

 Ex.2のコードですが、インスタンスを破棄している(破棄したいと思っている)のは開発者で、コードはインスタンスを破棄してはいません。Disposeメソッドは、アンマネージリソース解放を指示するメソッドで、インスタンスを破棄するメソッドではありません。.NET Frameworkにおいて、インスタンスを破棄するタイミングは不明です。CLRが管理しています。しかし、C#、C++ではデストラクタ、VB.NETではファイナライザを明示的に呼び出すことで、破棄することが出来ます。デストラクタ(ファイナライザ)で終了処理を呼び出すように定義していれば、インスタンスが破棄されるタイミングで終了処理が行われるため、クラス使用者は終了処理の実行を明示的に指示する必要はありません。もちろん、インスタンスの破棄は、明示的に行う必要があります。


> ところが、要件(3)のようなケースでは、Dispose()の中で
> コンストラクタで初期化したパラメータを使用しなくてはならない場合、
> 正常に処理することができません。
> Dispose()はいつ実行されるか分からないためコンストラクタで
> 再初期化した後の値が参照される可能性があります。
 ほとんど間違い。
 複数のインスタンスで共通する値は使われません。各インスタンスで変数の値は独立しています。そうでなければプログラムが成り立ちません。もっとも、クラス変数を定義していれば、クラス変数はすべてのインスタンスで共通して使われます。「ほとんど」としたのは、クラス変数なのか、インスタンス変数なのか、明示されていないからです。
 Disposeは、開発者が呼び出せば、その時に実行されます。逆に、呼び出さなければ、実行されません。したがって、『いつ実行されるかわからない』というのは誤り。ただし、IDisposableインタフェースを実装する場合、デストラクタ(ファイナライザ)でDiposeメソッドをコールし、確実にDiposeメソッドが実行されるようにする必要があります。このため、デストラクタ(ファイナライザ)が『いつ実行されるかわからない』ので、デストラクタ(ファイナライザ)で実行されるようにコード上はDisposeしなかったインスタンスのDisposeメソッドが、いつ実行されるかわからないという意味では正解。

 デストラクタ(ファイナライザ)と、Disposeは違います。注意してください。


 で、この案件ですが、どちらかというと、シングルトンパターンの応用???
コード:
' 未検証
Public Class SingleInstance

	Private Shared MyInstance As SingleInstance
	Private InitializedValue As Integer

	Private Sub New()
		' コンストラクタを公開しない
	End Sub

	Private Sub New(ByVal init As Integer)
		InitializedValue = init
	End Sub

	Public Shared Function _
	GetInstance(ByVal InitValue As Integer) As SingleInstance
		If Not MyInstance Is Nothing Then
			' インスタンス作成済みなら、
			' まず破棄する
			MyInstance.ClearInstance()
		End If
		' 新しいインスタンスを生成する
		MyInstance = New SingleInstance(InitValue)
		return MyInstance
	End Function

	Public Sub ClearInstance()
		If SingleInstance.MyInstance Is Nothing Then Return
		' TODO:終了処理
		SingleInstance.MyInstance = Nothing
	End Sub

	Public Sub Finalize()
		ClearInstance()
	End Sub

	' その他必要事項
End Class

' 使用
Dim si As SingleInstance
' コンストラクタが公開されていないので、次はエラー
' si = New SingleInstance()

' まったく新規
si = SingleInstance.GetInstance(123I)
' 123は自動的に終了される
si = SingleInstance.GetInstance(456I)
' 456の終了を明示することは必要
si.ClearInstance()


1

スキルアップ/キャリアアップ(JOB@IT)