我创建了一个事件处理程序,它只返回调用完成时从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);
}
}
Run Code Online (Sandbox Code Playgroud)
这是我订阅我的活动的地方:
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
}
Run Code Online (Sandbox Code Playgroud)
你可能两次添加代表,是否可能?
在这种情况下,问题不在于谁调用委托,而在于谁将委托添加到事件中.
也许你做了类似......
private Class1 instance1;
void callback(...)
{
}
void myfunction()
{
this.instance1.DataCalled += this.callback;
this.instance1.DataCalled += this.callback;
}
Run Code Online (Sandbox Code Playgroud)
如果没有,请尝试在订阅事件的位置添加断点,并查看是否调用了两次.
作为旁注,在调用事件时应始终检查null,如果没有订阅者,则可以获得NullReferenceException.我还建议您使用变量来存储事件委托,以避免多线程失败的风险.
public void CallData()
{
List<DataItem> dataItems = new List<DataItem>();
var handler = this.DataCalled;
if (handler != null)
handler(this, dataItems);
}
Run Code Online (Sandbox Code Playgroud)
编辑:既然现在我看到了代码,很明显,每次调用GetNewDataItems方法时,每次都要对事件进行处理.以这种方式,您只能订阅一次,例如,在构造函数中,或者将变量存储在某处或在完成时取消注册事件.
此代码还包含可能的内存泄漏:每次添加委托时,至少会保留包含事件的实例和包含预订方法的实例,直到两者都未被引用.
你可以试着这样做......
void dataHelper_DataCalled(object sender, List<DataItem> dataItemsList)
{
// Deregister the event...
(sender as Class1).DataCalled -= dataHelper_DataCalled;
//Do something with results
}
Run Code Online (Sandbox Code Playgroud)
但是,以这种方式,您必须确保在事件注册期间没有异常时将触发事件或您再次发生内存泄漏.
也许你只需要一个代表,而不是一个事件.当然,如果要释放委托,则应将委托字段设置为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); ...
Run Code Online (Sandbox Code Playgroud)