自定义事件处理程序被调用两次
本文关键字:两次 调用 事件处理 程序 自定义 | 更新日期: 2023-09-27 18:13:39
我创建了一个事件处理程序,它只返回调用完成时从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
}
可能您添加了两次委托,是可能的吗?
在这种情况下,问题不在于谁调用委托,而在于谁将委托添加到事件中。
可能你做了像…
private Class1 instance1;
void callback(...)
{
}
void myfunction()
{
this.instance1.DataCalled += this.callback;
this.instance1.DataCalled += this.callback;
}
如果没有,尝试在订阅事件的地方添加一个断点,并查看是否调用了两次。
作为旁注,在调用事件时应该始终检查是否为空,如果没有订阅者,则可以获得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); ...
代码中的以下行应该翻转。这就是
这些行
dataHelper.CallData();
dataHelper.DataCalled += new DataHelper.DataCalledEventHandler(dataHelper_DataCalled);
应:dataHelper.DataCalled += new DataHelper.DataCalledEventHandler(dataHelper_DataCalled);
dataHelper.CallData();
因为您首先需要附加事件处理程序,然后调用对象上可以引发事件的其他方法