【问题标题】:Raise event of object where the type implements an Interface引发类型实现接口的对象的事件
【发布时间】:2012-08-18 00:52:31
【问题描述】:

我在尝试引发对象的事件时遇到问题,该对象的类型实现了另一个类的接口(事件所在的位置)。

这是我的代码:

界面 IBaseForm

public interface IBaseForm
{
    event EventHandler AfterValidation;
}

实现IBaseForm接口的Form

public partial class ContactForm : IBaseForm
{
    //Code ...

    public event EventHandler AfterValidation;
}

我的控制器:发生错误的地方

错误信息:

AfterValidation 事件只能出现在左侧 += 或 -=(在 IBaseForm 类型中使用时除外)

public class MyController
{
    public IBaseForm CurrentForm { get; set; }

    protected void Validation()
    {
        //Code ....

        //Here where the error occurs
        if(CurrentForm.AfterValidation != null)
            CurrentForm.AfterValidation(this, new EventArgs());
    }
}

提前致谢

【问题讨论】:

    标签: c# .net events interface


    【解决方案1】:

    您不能从定义类之外引发事件。

    你需要在你的界面上创建一个Raise()方法:

    public interface IBaseForm
    {
        event EventHandler AfterValidation;
    
        void RaiseAfterValidation();
    }
    
    public class ContactForm : IBaseForm
    {
        public event EventHandler AfterValidation;
    
        public void RaiseAfterValidation()
        {
            if (AfterValidation != null)
                AfterValidation(this, new EventArgs());
        }
    }
    

    在你的控制器中:

    protected void Validation()
    {
        CurrentForm.RaiseAfterValidation();
    }
    

    但是,如果您想从定义类之外触发事件,这通常是一种设计味道……通常在IBaseForm 实现中应该有一些触发事件的逻辑。

    【讨论】:

    • 感谢您的回复,我实际上在我的应用程序中使用了 MEF,并且我正在尝试从主应用程序触发事件(插件中的谁)。有没有更好的办法?
    • @SidAhmed:不,你不能在包含它的类之外引发事件(你不应该这样做)。
    • @SidAhmed 没有其他方法可以从外部触发事件。我只是想指出您的设计可能存在一些问题。但是如果不看全貌就很难说。在插件系统的情况下,我认为我可以让主应用程序触发插件中的事件......
    • 好的,谢谢,我已经实现了您的解决方案,并且它有效;-)
    【解决方案2】:

    event 本质上是 .NET 中的一个结构,它标识两种方法——add 方法和remove 方法;每个都接受delegate 作为参数。

    虽然事件通常与将传入的委托组合在一起的方法一起使用,或者将传入的委托中删除,但类型为@987654323@的字段,这是一个实施细节。虽然创建事件的正常目的是让其他代码传入他们希望在某些情况下调用的委托,但 .NET 中的 事件 机制并没有关心什么,如果有的话,用传入的代表做了什么。

    在某些情况下,与事件关联的 addremove 方法简单地丢弃传入的委托可能是完全合法和合乎逻辑的。例如,不可变集合类型可能实现@987654324@(以便于与例如应该根据需要自动更新以显示当前集合状态的控件一起使用),但丢弃传递给更新通知事件的任何委托。如果集合永远不会更新,它就永远不需要使用订阅者列表,因此它没有理由保留一个。

    因为没有要求事件必须对传入的委托执行任何操作,所以 .NET 事件本身无法提供除了公开 addremove 之外的任何功能方法。

    虽然 .NET 中的 event 类型的描述符包括一个属性,该属性除了 add 之外还可以指定一个 raise 方法 >remove,该功能在实践中从未使用过。 .NET 的初步设计可能打算使用它,并且删除类型的 any 成员可能会在某些极端情况下(例如反序列化)导致兼容性问题,但在这些问题之外,该属性也可能不会存在。

    【讨论】:

    • 我正在写一些与 raise 方法 相关的内容,并且刚刚阅读了此内容。我发现 raise 方法 似乎对框架的设计没有用处,c# 根本不支持它,EventInfo.GetRaiseMethod 总是返回null
    • @KenKin:我认为在 CIL 中构造事件时可以指定“raise”方法。并不是说这样的事情有任何用处,但我认为记录中有空间,并且如果程序集出于某种原因定义了它,GetRaiseMethod 将报告程序集定义的内容。不是这样吗?
    • 据我了解,并非如此; c# 编译器不会为 raise 方法生成插槽,但 VB.Net 会。这并不奇怪,因为语言本身不支持这种语法。另一方面,raise 方法可以是可以正确调用事件处理程序的任意方法。我个人的猜测是c#团队认为raise方法应该是用户定义的方法,编译器根本不知道它是什么样子的。
    • @KenKin:VB.NET 有一个RaiseEvent 关键字,它只能在定义事件的对象中使用;自定义事件中的“raise”方法不会为GetRaiseEvent 生成添加元数据;它只是更改了 RaiseEvent 关键字调用的代码。顺便说一句,我真的不喜欢 C# 为委托和事件使用相同名称的方式。带有事件名称的调用样式语法不应该是委托调用;它应该被解释为“如果非空则调用事件委托,否则什么都不做”。这样的设计可以节省数百万行代码。
    • 哈,其实我对VB.Net不是很熟悉。正如我们可以看到这些语言之间事件引发的差异,c# 不支持它对我来说似乎是合理的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-09
    • 2016-02-01
    相关资源
    最近更新 更多