为什么我不能从扩展方法中调用PropertyChanged事件?

18

我试着编写一个类来避免使用"RaisePropertyChanged"这样的方法。我知道可以继承一个有这个实现的类,但在某些情况下我无法这样做。我试过使用扩展方法,但 Visual Studio 报错。

public static class Extension
{
    public static void RaisePropertyChanged(this INotifyPropertyChanged predicate, string propertyName)
    {
        if (predicate.PropertyChanged != null)
        {
            predicate.PropertyChanged(propertyName, new PropertyChangedEventArgs(propertyName));
        }
    }
}

它说:

"事件 'System.ComponentModel.INotifyPropertyChanged.PropertyChanged' 只能出现在 += 或 -= 的左边""


1
谢谢您提出这个问题。我也在尝试做同样的事情(INotifyPropertyChanged的mixin),并遇到了同样的问题。 - KChaloux
4个回答

20

Reed 是正确的。然而,我理解你想要做什么(使你的代码可重用——做得好!);我只是想指出,这通常可以通过接受 PropertyChangedEventHandler 委托 本身并从 INotifyPropertyChanged 实现中传递它来轻松解决:

public static void Raise(this PropertyChangedEventHandler handler, object sender, string propertyName)
{
    if (handler != null)
    {
        handler(sender, new PropertyChangedEventArgs(propertyName));
    }
}

接下来,在实现 INotifyPropertyChanged 接口的类中,你可以这样调用此扩展方法:

PropertyChanged.Raise(this, "MyProperty");

这段代码可以正常运行,因为在声明事件的类中,就像Marc所说,你可以像使用字段一样访问它(这意味着你可以将其作为委托参数传递给方法,包括扩展方法)。


1
你可以更加通用化,只需使用一个 EventHandler<T>。参见此类中的Raise<T>方法的示例:http://thehelpertrinity.codeplex.com/SourceControl/changeset/view/53656#2260 - Kent Boogaart
@Kent:我完全同意;唯一让人烦恼的是,在很多BCL类型中,你实际上没有泛型“EventHandler<T>”-based事件,而是这些奇怪的强类型委托(例如,“PropertyChangedEventHandler”),所以你必须要么接受某些转换参数来将“PropertyChangedEventHandler”到“EventHandler<PropertyChangedEventArgs>”进行平凡的lambda转换,或者更现实的是,你只需要实现你实际需要的内容 ;) - Dan Tao

11
你只能在定义事件的类内部触发该事件。
这一行:
predicate.PropertyChanged(propertyName, new PropertyChangedEventArgs(propertyName));

predicate不是定义此扩展方法的类,因此会失败。

这部分原因是通常使用基类而不是使用扩展方法来处理此问题。


4

事件实际上只是一个具有添加/删除功能的API(在CLI中确实有可选的“调用”功能,但C#编译器不提供它)。

你所拥有的是一个类似于字段的事件;字段式事件在声明类型外部作为添加/删除API,而在声明类型内部仅在必要时才像字段一样运作 - 最常见的情况是:调用订阅者(这是C# 4中的一个细微变化;在C# 4之前,从类型内部的所有访问都针对字段,包括+=/-=)。

按照定义,扩展方法不能在声明类型内部 - 只有顶级(非嵌套)静态类可以提供扩展方法;因此,没有扩展方法可以直接调用类似于字段的事件。


2
当你使用event关键字定义一个事件时,C#编译器实际上会在幕后为你生成许多东西。
event EventHandler MyEvent;

翻译后大致意思是(尽管实际上会添加一些锁定和其他检查,但与此类似)...

private EventHandler _myEvent;

public EventHandler MyEvent
{
  add( EventHandler handler )
  { 
    _myEvent += handler;
  }
  remove( EventHandler handler )
  {
    _myEvent -= handler;
  }
}

从上面可以看出,实际的事件处理程序 _myEvent 是私有的,不能直接调用。


1
吹毛求疵的一点:仅在未提供add/remove实现的情况下才会生成类似字段的事件(如上述)。 - Marc Gravell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接