- PR -

VB.NET によるイベント ハンドラのについて

投稿者投稿内容
りばぁ
大ベテラン
会議室デビュー日: 2003/11/26
投稿数: 130
お住まい・勤務地: 愛知県
投稿日時: 2004-02-12 15:27
引用:

Jittaさんの書き込み (2004-02-12 15:13) より:
 あ、いや、「AddHandlerができなかった」のですが、この記述ですと、「できない」とは書いていないですよね。。。
 タイマー起動中のインターバル変更は、「変更したときからカウントされなおします」と書いてあります。



えと、できないというか、

http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/vbcn7/html/vaconUnderstandingEventHandlers.asp

には、

>AddHandler ステートメントと RemoveHandler ステートメントは、Handles 句よりも柔軟性があります。これらのステートメントを使用すると、実行時に 1 つ以上のイベント ハンドラに対してイベントを動的に接続および接続解除できます。また、WithEvents を使用してオブジェクト変数を宣言する必要はありません。

と書いてありますよ、ということが言いたかったのです^^;

あと、タイマーを起動したままIntervalの変更は問題ないようですね^^;
申し訳ないです。
たしかに、ヘルプには何も書いてなかったのでどうかとは思ったんですが、

> タイマー起動中のインターバル変更は、「変更したときからカウントされなおします」と書いてあります。

そうだったのですね・・。

--追加

引用:

原因は、プロセスのExitedイベントです(と思います)。
なぜかというと、うーん、コントロールはUIスレッド以外から操作してはならないという制限に引っかかるからです。



あ、これはヘルプにも書いてありますね・・

>Timer は、ユーザー定義の間隔でイベントを発生させるために使用されます。この Windows タイマは、UI スレッドを使用して処理を実行するシングルスレッド環境に合わせて設計されています。ユーザー コードには利用できる UI メッセージ ポンプが必要です。また、このコードは必ず同じスレッドから操作し、別のスレッドに対する呼び出しをマーシャリングする必要があります。

の部分でしょうか。




[ メッセージ編集済み 編集者: りばぁ 編集日時 2004-02-12 15:41 ]
ナキヲ
常連さん
会議室デビュー日: 2003/08/22
投稿数: 32
お住まい・勤務地: 京都・自宅から勤務地まで自転車で40分
投稿日時: 2004-02-12 15:43
引用:
これを回避するには、ProcessのSynchronizingObjectにフォームのインスタンスをセットするか、イベントハンドラからフォームのInvokeを使用してタイマーを操作するか辺りになります。



Process.SynchronizingObjectのヘルプを見て納得です。
勉強になりました。
よねKEN
ぬし
会議室デビュー日: 2003/08/23
投稿数: 472
投稿日時: 2004-02-12 16:02
引用:

なっくさんの書き込み (2004-02-12 13:17) より:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
'作成したクラスにイベントを実装しました
Dim clsTest As New clsSample
'RaiseEventでイベントを実装
AddHandler clsTest.evtEvent, AddressOf Sample1

clsTest.Action()

End Sub



解決したようでなによりです。
ところで、本題とは関係ないんですが、この記述はあまりよろしくないのではないでしょうか。

変数clsTestはButton1_Clickメソッド内のローカル変数なので、
Actionメソッドを実行し、メソッドを抜けた時点でNothingになるため、
このclsSampleクラスのインスタンスの参照を保持するものはなくなると思います。

となると、evtEventイベントが発生する前に、ガベージコレクションが働くと、
その後、evtEventイベントは二度と発生しないことになります。
Yun
会議室デビュー日: 2004/02/10
投稿数: 13
投稿日時: 2004-02-12 17:10
みなさん、ありがとうございます。

返答遅れてすみません。
実はBBSに2ページ目があるとは気づかなかったもので・・・
面目ないですm(__)m

引用:


原因は、プロセスのExitedイベントです(と思います)。
なぜかというと、うーん、コントロールはUIスレッド以外から操作してはならないという制限に引っかかるからです。

これを回避するには、ProcessのSynchronizingObjectにフォームのインスタンスをセットするか、イベントハンドラからフォームのInvokeを使用してタイマーを操作するか辺りになります。




Exitedが原因だったのですね!!
Process.SynchronizingObjectは大変勉強になりました。
完全に理解できた訳ではないのでこれからHelpと睨めっこしようと
おもいます(笑

引用:

変数clsTestはButton1_Clickメソッド内のローカル変数なので、
Actionメソッドを実行し、メソッドを抜けた時点でNothingになるため、
このclsSampleクラスのインスタンスの参照を保持するものはなくなると思います。

となると、evtEventイベントが発生する前に、ガベージコレクションが働くと、
その後、evtEventイベントは二度と発生しないことになります。



詳しくはこれから調べて検証しないといけないのですが
メソッドを抜けた時点でNothingになってもプロセスが残っている為、
プロセスが無くなるまでガベージコレクションは働かない様な気がします。
ただ確証がないのでローカル変数で使わない方向にするかもしれませんです。

今回はほんと勉強になりました。
お時間を割いて頂き、皆さま本当にありがとうございました。
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2004-02-12 18:09
引用:

なっくさんの書き込み (2004-02-12 17:10) より:

メソッドを抜けた時点でNothingになってもプロセスが残っている為、
プロセスが無くなるまでガベージコレクションは働かない様な気がします。
ただ確証がないのでローカル変数で使わない方向にするかもしれませんです。


 その認識は違います。ガベージコレクションは、「いつ動くかわかりません」。システム(.NET Framework?)がマネージメモリが足りないと判断した時点で動きます。または、明示的にGC.Collectメソッドを呼び出すか。

#だから、AddHandlerができないとエラーがでた、のだと思っていたのだが。。。
Yun
会議室デビュー日: 2004/02/10
投稿数: 13
投稿日時: 2004-02-12 19:29
<今回のまとめ>
皆様のおかげで無事成功しました。ありがとうございます。
SetSynchronizingObjectを使用たソースを載せておきます。

以下変更コード↓↓↓
コード:
<clsSampleにて>
    Public Overridable Sub SetSynchronizingObject(ByVal myObj As Object)
        'イベント ハンドラ呼び出しをマーシャリング
        SampleProcess.SynchronizingObject = myObj
    End Sub

<Formにて>
    Private Sub Button1_Click(ByVal sender As System.Object, _
                              ByVal e As System.EventArgs) Handles Button1.Click

        '作成したクラスにイベントを実装しました 
        Dim clsTest As New clsSample

        '追加コード↓↓↓
        '(コンポーネントが作成されているスレッドと同じスレッドで
        ' Exitedイベントを処理するメソッドを呼び出す)
        clsTest.SetSynchronizingObject(Me)

        'RaiseEventでイベントを実装 
        AddHandler clsTest.evtEvent, AddressOf Sample1

        clsTest.Action()

    End Sub




Jitta様、返信ありがとうございます。
引用:

 その認識は違います。ガベージコレクションは、「いつ動くかわかりません」。システム(.NET Framework?)がマネージメモリが足りないと判断した時点で動きます。または、明示的にGC.Collectメソッドを呼び出すか。


確かにGCはいつ動くか分かりませんね^^;
クラスのデバック中にいきなりFinalize走ることもありますし。
でも、確かManageObjectで参照があればGCで開放されなかったような気がします。

以下参考文献↓↓↓
http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/cpref/html/frlrfSystemGCClassTopic.asp

上記URLから抜粋
引用:

ガベージ コレクションは、次に示すステップを実行します。

1.ガベージ コレクタは、マネージ コードで参照されるマネージ オブジェクトを検索します。
2.ガベージ コレクタは、参照されないオブジェクトの終了を試みます。
3.ガベージ コレクタは、参照されないオブジェクトを解放し、それらのオブジェクトのメモリを収集します。
コレクションの実行中、マネージ コード内のオブジェクトへの 1 つ以上の参照が見つかった場合、ガベージ コレクタはそのオブジェクトを解放しません。ただし、ガベージ コレクタは、アンマネージ コードからのオブジェクトへの参照は認識しないため、アンマネージ コードで排他的に使用されているオブジェクトを解放しないように指定されていない限りは、そのようなオブジェクトを解放してしまう場合があります。



上記「コレクションの実行中、マネージ コード内のオブジェクトへの 1 つ以上の参照が見つかった場合、ガベージ コレクタはそのオブジェクトを解放しません。」
とあります。
今回はclsSampleを開放しても
プロセスの参照が残っている・プロセスはManageObjectの為
GCでの開放は行われないと認識しています。
検証としては不十分ですがプロセスが開放された時、ランダムでFinalizeが走るのを確認しています。


何分、GCは勉強中なので間違っていたらすみません。
架空兎
ベテラン
会議室デビュー日: 2003/08/18
投稿数: 78
お住まい・勤務地: さいたま氏
投稿日時: 2004-02-12 20:08
引用:

よねKENさんの書き込み (2004-02-12 16:02) より:

変数clsTestはButton1_Clickメソッド内のローカル変数なので、
Actionメソッドを実行し、メソッドを抜けた時点でNothingになるため、
このclsSampleクラスのインスタンスの参照を保持するものはなくなると思います。


MSDN ライブラリ 「Visual Basic 言語の仕様 - 6.9 デリゲート」より

引用:

デリゲートは Shared メソッドとインスタンス メソッドの両方を参照できます。後者の場合、デリゲートはメソッドのエントリ ポイントへの参照だけでなく、メソッドを呼び出すオブジェクト インスタンスへの参照も格納します。


ということらしいので、clsSample クラスの Action メソッドで
>AddHandler SampleProcess.Exited, AddressOf processExited
をしているため、clsSample クラスのインスタンスはデリゲートに格納されているらしいです。
つまり、SampleProcess が実行中である限り、ガベージコレクションの
対象にはならないと思われます。


>なっくさんへ

解決したあとで申し訳ないのですが、^^;
System.Windows.Forms.Timer クラスの代わりに
System.Timers.Timer クラスを使うという手も一応はあると思います。
#一応は大丈夫っぽいです。
なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2004-02-12 20:33
引用:

架空兎さんの書き込み (2004-02-12 20:08) より:
ということらしいので、clsSample クラスの Action メソッドで
>AddHandler SampleProcess.Exited, AddressOf processExited
をしているため、clsSample クラスのインスタンスはデリゲートに格納されているらしいです。
つまり、SampleProcess が実行中である限り、ガベージコレクションの
対象にはならないと思われます。


えーここの辺りを確認するには、最終的に参照がルートにつながっているかを考える必要があります。
>AddHandler SampleProcess.Exited, AddressOf processExited
これにより、SampleProcessオブジェクトには、clsSampleへの参照が含まれていることになります。
したがって、SampleProcessオブジェクトが生きている(ここでは破棄されたという意味ではなく、参照が残っているという意味で)限り、clsSampleは生きていることになります。
では、SampleProcessオブジェクトの参照は誰が保持しているでしょうか?clsSample自身ですね。すなわち循環参照であり、ルートにつながっていないため、これ自体は意味を持ちません
では、clsSampleを誰か他の場所から参照しているでしょうか?
Button1_Clickメソッドが終了すると、もはや誰も参照していませんね。

さて、Button1_Clickメソッドが終了した後で、たまたまGCが発生したとします。

ここでGCは、ルートから全てのオブジェクトをたどって、オブジェクトが有効か調べます。どこからもSampleProcessおよびclsSampleにはたどり着きませんので、GCの対象になります。

というわけで、やっぱりまずいんではと思うわけですが、どこかに見落としがあるような気がしないでもありません。
まあ、正直なところで言うと、「怪しい or 自信のないことはするな」ということで、私はやらないです…

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