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

标签 c# .net events

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

现在我继续在 Debug模式下运行该应用程序,发现第一次调用该事件时它运行良好,但在它完成后立即第二次触发该事件。我已经检查过并且绝对确定我不会在接收器类中多次调用该事件。

这是我第一次尝试在我的应用程序中创建自定义事件处理程序,因此我不能完全确定实现是否 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
    }

最佳答案

可能你添加了两次委托(delegate),这可能吗?

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

可能你做了类似...

private Class1 instance1;

void callback(...)
{
}

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

如果没有,尝试在你订阅事件的地方加个断点,看看它是否被调用了两次。

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

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

编辑:因为现在我看到了代码,很明显,每次您调用 GetNewDataItems 方法时,您每次都会订阅该事件。 以这样的方式您只订阅一次,例如,在构造函数中,或将您的变量存储在某处或在您完成后注销事件。

此代码还包含一个可能的内存泄漏:每次添加委托(delegate)时,都会使包含事件的实例和包含订阅方法的实例保持事件状态,至少,直到两者都未被引用。

你可以尝试做这样的事情......

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

    //Do something with results
}

然而,通过这种方式,您必须确保如果在事件注册期间没有异常,事件将被触发,否则您将再次发生内存泄漏。

也许您只需要一个委托(delegate)而不是一个事件。当然,当你想释放委托(delegate)时,你应该将你的委托(delegate)字段设置为 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);    ...

关于c# - 自定义事件处理程序被调用两次?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8020940/

相关文章:

c# - 如何等待 Task.ContinueWith 内部任务完成

c# - 如何将 byte[] 数组加载到 C# 中的结构中?

.net - 是否安装了 .NET 版本的 DSC 资源?

c++ - 下沉的 DWebBrowserEvents2 事件似乎挂起编程导航

javascript - 如何禁用由子元素触发的 mouseout 事件?

c# - ASP.NET Core Identity 2.0 - 将 401 状态代码返回到 ajax 函数

c# - 如何在C#中使用Nest获取所有索引并过滤索引

c# - 如果等待的任务链上有 'unwrapped'任务,则Task.Result/wait(..)无限期等待,而如果使用 'async/await',则成功完成

javascript - Javascript 是否可以在事件上下文中更改全局变量的值?

c# - Xamarin Forms Span 保留空白