自定义事件处理程序被调用两次?

时间:2011-11-05 14:53:53

标签: c# .net events

我创建了一个事件处理程序,它只返回调用完成时从Web服务收到的对象列表。

现在我继续以调试模式运行应用程序并发现第一次调用该事件时它运行正常,但在完成后立即被触发第二次。我已经检查过了,我绝对相信我不会在接收器类中多次调用该事件。

这是我在应用程序中创建自定义事件处理程序的第一步,因此我不完全确定实现是100%准确。

可能导致此问题的任何想法?我创建事件处理程序的方式是否准确?

这是DataHelper类

public class DataHelper
{
    public delegate void DataCalledEventHandler(object sender, List<DataItem> dateItemList);
    public event DataCalledEventHandler DataCalled;

    public DataHelper()
    {

    }

    public void CallData()
    {
        List<DataItem> dataItems = new List<DataItem>();
        //SOME CODE THAT RETURNS DATA
        DataCalled(this, dataItems);
    }
}

这是我订阅我的活动的地方:

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
   GetNewDataItems();
}
private void GetNewDataItems()
        {

                try
                {
                    DataHelper dataHelper = new DataHelper();
                    dataHelper.CallData();
                    dataHelper.DataCalled += new DataHelper.DataCalledEventHandler(dataHelper_DataCalled);

                }
                catch
                {
                   //Handle any errors
                }
            }
    }

    void dataHelper_DataCalled(object sender, List<DataItem> dataItemsList)
    {
        //Do something with results
        //NOTE: THIS IS WHERE THE EXCEPTION OCCURS WHEN EVENT IS FIRED FOR SECOND TIME
    }

2 个答案:

答案 0 :(得分:8)

可能你已经两次添加了代理,是否可能?

在这种情况下,问题不在于谁调用委托,而在于谁将委托添加到事件中。

可能你做过类似的事情......

private Class1 instance1;

void callback(...)
{
}

void myfunction()
{
    this.instance1.DataCalled += this.callback;
    this.instance1.DataCalled += this.callback;
}

如果没有,请尝试在订阅事件的位置添加一个断点,看看它是否被调用两次。

作为旁注,在调用事件时应始终检查null,如果没有订阅者,则可以获得NullReferenceException。 我还建议您使用变量来存储事件委托,以避免多线程失败的风险。

public void CallData()
{
    List<DataItem> dataItems = new List<DataItem>();
    var handler = this.DataCalled;
    if (handler != null)
        handler(this, dataItems);
}

编辑:从现在开始我看到代码,很明显,每次调用GetNewDataItems方法时,每次都要对事件进行处理。 以这种方式,您只能订阅一次,例如,在构造函数中,或者将变量存储在某处或在完成时取消注册事件。

此代码还包含可能的内存泄漏:每次添加委托时,至少会保留包含事件的实例和包含预订方法的实例,直到两者都未被引用。

你可以尝试这样做......

void dataHelper_DataCalled(object sender, List<DataItem> dataItemsList)
{
    // Deregister the event...
    (sender as Class1).DataCalled -= dataHelper_DataCalled; 

    //Do something with results
}

但是,通过这种方式,您必须确保在事件注册期间没有异常时将触发事件或者您再次发生内存泄漏。

也许你只需要一个代表,而不是一个事件。当然,如果要释放委托,则应将委托字段设置为null。

// in data helper class

private DataHelper.DataCalledEventHandler myFunctor;

public void CallData(DataHelper.DataCalledEventHandler functor)
{
    this.myFunctor = functor;
    //SOME CODE THAT RETURNS DATA
}

// when the call completes, asynchronously...
private void WhenTheCallCompletes()
{
    var functor = this.myFunctor;
    if (functor != null)
    {
        this.myFunctor = null;
        List<DataItem> dataItems = new List<DataItem>();
        functor(this, dataItems);
    }
}
    
// in your function
...    dataHelper.CallData(this.dataHelper_DataCalled);    ...

答案 1 :(得分:2)

应该翻转代码中的以下行。那是

这些行

dataHelper.CallData();
dataHelper.DataCalled += new DataHelper.DataCalledEventHandler(dataHelper_DataCalled);

应该是:

dataHelper.DataCalled += new DataHelper.DataCalledEventHandler(dataHelper_DataCalled);
dataHelper.CallData();

因为您首先需要附加事件处理程序,然后调用可以引发事件的对象上的其他方法