从所属类外部调用C#事件?

时间:2020-03-06 14:28:56  来源:igfitidea点击:

在任何情况下都能做到这一点吗?

我目前的情况是这样的:

public class CustomForm : Form
{
    public class CustomGUIElement
    {
    ...
        public event MouseEventHandler Click;
        // etc, and so forth.
    ...
    }

    private List<CustomGUIElement> _elements;

    ...

    public void CustomForm_Click(object sender, MouseEventArgs e)
    {
        // we might want to call one of the _elements[n].Click in here
        // but we can't because we aren't in the same class.
    }
}

我的第一个想法是要具有类似于以下内容的功能:

internal enum GUIElementHandlers { Click, ... }
internal void CustomGUIElement::CallHandler(GUIElementHandler h, object[] args) {
    switch (h) {
        case Click:
            this.Click(this, (EventArgs)args[0]);
            break;
        ... // etc and so forth
    }
}

这是一个丑陋的丑角,但它应该可以工作...不过必须有一个更优雅的解决方案吗? .NET库始终使用消息处理程序和Control中的调用事件来执行此操作。还有其他人有其他/更好的主意吗?

解决方案

我们确实应该将要能够从外部执行的代码包装在一个方法中。然后,该方法可以执行事件将执行的任何操作,并且该事件也将调用该方法。

我们只需要添加一个公共方法来调用该事件。 Microsoft已经对某些事件执行了此操作,例如对暴露Click事件的控件执行PerformClick。

public class CustomGUIElement    
{
    public void PerformClick()
    {
        OnClick(EventArgs.Empty);
    }

    protected virtual void OnClick(EventArgs e)
    {
        if (Click != null)
            Click(this, e);
    }
}

然后,我们将在示例事件处理程序中执行以下操作……

public void CustomForm_Click(object sender, MouseEventArgs e)        
{
    _elements[0].PerformClick();
}

c中的event关键字修改了委托的声明。它防止直接分配给委托(我们只能在事件上使用+ =和-=),并且防止从类外部调用委托。

因此,我们可以将代码更改为如下所示:

public class CustomGUIElement
{
...
    public MouseEventHandler Click;
    // etc, and so forth.
...
}

然后,我们可以像这样从类外部调用事件。

myCustomGUIElement.Click(sender,args);

缺点是使用该类的代码可以很容易地用以下代码覆盖任何已注册的处理程序:

myCustomGUIElement.Click = null;

如果将Click委托声明为事件,则不允许这样做。