如何在 C# 中正确重置自定义事件?

How to correctly reset a custom event in C#?

我有这个代码:

private void loadGENIOFileToolStripMenuItem_Click(object sender, EventArgs e)
{
    OpenFileDialog dlgFile = new OpenFileDialog();

    dlgFile.InitialDirectory = Properties.Settings.Default.PreviousPath;
    dlgFile.Title = "Select GENIO file";
    dlgFile.Filter = "GENIO files (*.txt)|*.txt";
    dlgFile.FilterIndex = 0;
    dlgFile.Multiselect = false;

    if (dlgFile.ShowDialog() == DialogResult.OK)
    {
        Properties.Settings.Default.PreviousPath = Path.GetDirectoryName(dlgFile.FileName);

        DeleteView();

        m_oThreadServices.OnLoadingCompleted += (_sender, _e) =>
        {
            mruMenu.AddFile(dlgFile.FileName);
            m_sUITInfo.dbDatabase = m_oThreadServices.GetDatabase();
            CreateView();
        };

        m_oThreadServices.SetGenioFilePath(dlgFile.FileName);
        m_oThreadServices.start();
    }
}

但我也在尝试实现 MRU 处理程序:

    private void OnMruFile(int number, String filename)
    {
        if (File.Exists(filename))
        {
            Properties.Settings.Default.PreviousPath = Path.GetDirectoryName(filename);

            DeleteView();

            m_oThreadServices.OnLoadingCompleted += (_sender, _e) =>
            {
                mruMenu.SetFirstFile(number);
                m_sUITInfo.dbDatabase = m_oThreadServices.GetDatabase();
                CreateView();
            };

            m_oThreadServices.SetGenioFilePath(filename);
            m_oThreadServices.start();
        }
        else
            mruMenu.RemoveFile(number);
    }
}

我的 m_oThreadServices.OnLoadingCompleted 代码行似乎要求我使用 += 结果,如果我首先 加载 一个文件,它会添加第一个事件处理程序.如果我然后去使用 MRU 列表加载不同的文件,它最终会 运行 two OnLoadingCompleted 处理程序。

我试过m_oThreadServices.OnLoadingCompleted =但它不允许。那么什么是我拦截事件处理程序而不是最终调用两组代码的正确方法呢?我做错了吗?

谢谢。

没有直接的方法可以从处理程序中删除匿名或未知事件。但是,您可以查看 MSDN 上的这个论坛帖子:https://social.msdn.microsoft.com/Forums/vstudio/en-US/45071852-3a61-4181-9a25-068a8698b8b6/how-do-i-determine-if-an-event-has-a-handler-already?forum=netfxbcl

有一些关于使用反射从事件处理程序中删除委托的代码和讨论。

不过,准确了解您想要完成的任务可能会更好。也许有更好的方法来获得您正在寻找的最终结果,而不是重新连接事件。

删除已建立的事件代码以更改要实现的代码的行为通常不是好的做法。它可能导致意想不到的后果和不稳定的行为。如果定义了事件代码,最好将其保留在适当的位置并围绕它设计您的应用程序。

另一方面,如果这是您添加的代码,或者在您的代码库中,您可以将其删除,前提是您已进行适当的研究以验证其删除并且不会导致应用程序中断别处。最好的方法是将事件代码放在命名函数中:

public void MyEventCode(object sender, EventArgs args)
{
   // Do event stuff..
}

然后您可以按名称删除事件:

control.DoMyEvent -= MyEventCode;

所以 += 基本上是在事件中调用 Combine 的语法糖。委托存储在调用列表中,触发事件时的默认行为是调用列表中的每个委托都按照它们添加的顺序被调用。这就是为什么您不能简单地将 OnLoadingCompleted 设置为一个带有 = 符号的委托 - 事件存储委托列表,而不是一个。

您可以使用 -= 删除委托(调用 Remove 的语法糖)。也许您想在某处正式声明前一个委托而不是将其作为 lambda 传递。这将使您在完成后将其删除。

如果是命名函数,您可以删除处理程序:

private void OnLoadingComplete_AddFile(_sender, _e) 
{
    mruMenu.AddFile(dlgFile.FileName);
    m_sUITInfo.dbDatabase = m_oThreadServices.GetDatabase();
    CreateView();
}

...

m_oThreadServices.OnLoadingCompleted += OnLoadingComplete_AddFile;

...

m_oThreadServices.OnLoadingCompleted -= OnLoadingComplete_AddFile;

删除尚未添加(或已删除)的处理程序是空操作,因此您可以在添加处理程序之前删除 "other" 处理程序:这将确保有大多数处理程序。

您应该确保在引发事件后您的事件处理程序已从事件源取消订阅。

为此,您必须稍微修改一下匿名处理程序。例如,这个片段:

m_oThreadServices.OnLoadingCompleted += (_sender, _e) =>
{
    mruMenu.AddFile(dlgFile.FileName);
    m_sUITInfo.dbDatabase = m_oThreadServices.GetDatabase();
    CreateView();
};

应该是这样的:

EventHandler onLoadingCompleted = null;
onLoadingCompleted = (_sender, _e) =>
{
    m_oThreadServices.OnLoadingCompleted -= onLoadingCompleted;
    mruMenu.AddFile(dlgFile.FileName);
    m_sUITInfo.dbDatabase = m_oThreadServices.GetDatabase();
    CreateView();
};
m_oThreadServices.OnLoadingCompleted += onLoadingCompleted;

另一个也一样。

EventHandler onLoadingCompleted = null;

需要避免在此处使用未初始化的变量编译器错误

m_oThreadServices.OnLoadingCompleted -= onLoadingCompleted;