イベント ハンドラーで変数の範囲を適切に設定する
イベント ハンドラーのプログラミングでよくある誤りは、イベント ハンドラーを接続するオブジェクトが、イベント処理の目的には限定されすぎたスコープで宣言されていることです。 オブジェクトの有効期間の範囲には、オブジェクトのイベント ハンドラーとしてコールバック メソッドを接続する関数だけでなく、イベントが実際に処理されるコールバック メソッド自体も含まれる必要があります。 そのようにしないと、オブジェクトがスコープの範囲外になり、コールバック メソッドで定義されなくなった場合、コールバック メソッドは呼び出されず、イベントは意図したように処理されません。
次の例では、MyNewInspector コールバック メソッドを NewInspector イベントに接続しようとしています。 しかし、このコード サンプルのコールバック メソッドは、 Connect 関数に限定されたスコープを持つ Inspectors オブジェクトの NewInspector イベントに対してフックされます。 このコールバック メソッドが最終的に呼び出されるときには、Connect 関数は既に終了していて、Inspectors オブジェクトは既にガベージ コレクトされていてるため、MyNewInspector が呼び出されることはありません。
using Outlook = Microsoft.Office.Interop.Outlook;
class MyClass
{
private Outlook.Application MyApp;
public MyClass(Outlook.Application appOutlook)
{
MyApp = appOutlook;
}
// Connects the NewInspector event to my callback method
public void Connect()
{
MyApp.Inspectors.NewInspector += new Outlook.
InspectorsEvents_NewInspectorEventHandler(
MyNewInspector);
}
public void MyNewInspector(Outlook.Inspector inspector)
{
MessageBox.Show("
My event handler caught a NewInspector event");
}
}
この場合の正しい方法は、有効期間が MyNewInspector コールバック メソッドを含む MyClass 全体におよぶ、もっと存続期間の長い変数に、Inspectors オブジェクトを格納することです。 次の例では、MyInspectors のスコープは MyClass 全体であり、コールバック メソッドがクラスの有効期間に接続されることが保証されます。
using Outlook = Microsoft.Office.Interop.Outlook;
class MyClass
{
private Outlook.Application MyApp;
private Outlook.Inspectors MyInspectors;
public MyClass(Outlook.Application appOutlook)
{
MyApp = appOutlook;
}
// Connects the NewInspector event to my callback method
public void Connect()
{
MyInspectors = MyApp.Inspectors;
MyInspectors.NewInspector += new Outlook.
InspectorsEvents_NewInspectorEventHandler(
MyNewInspector);
}
public void MyNewInspector(Outlook.Inspector inspector)
{
MessageBox.Show("
My event handler caught a NewInspector event");
}
}
各種の言語でイベント ハンドラーに接続する方法には構文的な違いがあるため、Visual Basic のように親オブジェクトのインスタンスを指定しているイベントを接続し、同時にコールバック メソッドを定義できる言語では、この問題はほとんど発生しません。 Visual Basic の次の例では、Handles キーワード (keyword)を使用して、Region_Expanded コールバック メソッドを Expanded イベントに接続します。 親オブジェクト Region のインスタンスには、Region_Expanded コールバック メソッドを含む MyClass にまたがるスコープがあります。
Imports Outlook = Microsoft.Office.Interop.Outlook
Public Class MyClass
' The Region object has a lifetime spanning the class
' including the callback method Region_Expanded
Private WithEvents Region As Outlook.FormRegion
...
Private Sub Region_Expanded() Handles Region.Expanded
MsgBox("My EventHandler caught an Expanded event.")
End Sub
End Class
この例では、Region_Expanded コールバック メソッドがクラスの有効期間中に Expanded イベントに接続されているため、コールバック メソッドが必要に応じて呼び出されます。