Поделиться через


Правильное задание области применения в обработчиках событий

Распространенной ошибкой при программировании обработчиков событий является подключение обработчика события к объекту, который был объявлен с областью применения, слишком ограниченной для целей обработки этого события. Время жизни объекта не должно ограничиваться только функцией, подключающей метод обратного вызова в качестве обработчика события для объекта, но распространяться и на сам метод обратного вызова, в котором фактически обрабатывается событие. В противном случае, если объект оказывается вне области применения и больше не определен в методе обратного вызова, метод обратного вызова не вызывается и событие не обрабатывается нужным образом.

В следующем примере предпринимается попытка подключить метод обратного вызова MyNewInspector к событию NewInspector. Но метод обратного вызова в примере кода подключается к событию NewInspector объекта Inspectors, область применения которого ограничена функцией Connect. В результате, когда вызывается метод обратного вызова, программа уже вышла из функции 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");
    }
}

. В следующем примере 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 для подключения метода обратного вызова Region_Expanded к событию Expanded. Экземпляр родительского объекта Region имеет область, охватывающий MyClass, включая метод обратного вызова Region_Expanded.

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 в течение времени существования класса, метод обратного вызова вызывается соответствующим образом.

См. также