实施 INotifyPropertyChanged 的​​差异

Differences in Implementing INotifyPropertyChanged

我正在学习 WPF,并且仍在尝试完全理解 INotifyPropertyChanged 接口。我找到了以下示例:

public event PropertyChangedEventHandler PropertyChanged;

public void NotifyPropertyChanged(string propertyName)
{
    if (this.PropertyChanged != null)
    {
        this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)
    }   
}

该示例遵循我在其他代码中经常看到的相同格式。在 Visual Studio 2013 年,我可以选择允许 IDE 显式为我实现接口。这样做会创建以下内容:

   event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
   {
       add { throw new NotImplementedException(); }
       remove { throw new NotImplementedException(); }
   }

这两个有什么不同?如果我决定使用 Visual Studio 生成的代码,与第一个示例相比会怎样?

我建议阅读 .NET 中的事件和事件处理程序。

我们称之为 A

public event PropertyChangedEventHandler PropertyChanged;

还有这个B

event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
{
    add { throw new NotImplementedException(); }
    remove { throw new NotImplementedException(); }
}

相似,但也有不同。

  1. A PropertyChanged 是显式的(您已经为其指定了访问修饰符)public,而 B PropertyChanged 是隐式的 public,因为您正在为一个public接口。
  2. A PropertyChanged 实现默认 add/remove,它们只是 PropertyChanged += handlerProeprtyChanged -= handler 的包装器,其中 B PropertyChanged 实现 add/remove作为 throw new exception。因此,如果有任何尝试订阅 B 'PropertyChanged' 事件,它将抛出 NotImplementedException.
  3. 任何使用您的 class 或您的 class 作为 INotifyPropertyChanged 的人都可以访问 PropertyChanged 活动,而 B PropertyChanged 活动将 只有 在有人将您的 class 投射到 INotifyPropertyChanged 后才能访问。

现在你的另一段代码:

public void NotifyPropertyChanged(string propertyName)
{
    if (this.PropertyChanged != null)
    {
        this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)
    }   
}

用于调用 PropertyChanged 事件(如果不为空),并通知任何订阅者提供者 propertyName 已更改。每当您想从外部代码调用 class 上的事件时,您都需要,因为事件可以 ONLY 从 class.

中调用

这里确实有两个问题。首先,事件中的添加和删除是什么?其次,什么是显式接口实现?这些是正交的东西。

添加和删除事件

在 C# 中,事件实际上很像属性。一个事件有一个 add 方法和一个 remove 方法。如果您没有明确指定它们,编译器将为您创建它们以及一个委托字段来支持事件(您可以通过引用事件从您自己的 class 中访问)。

如果愿意,您可以自己创建 addremove 方法(如果您想显式实现接口,则 必须 )。在这种情况下,您通常还会创建一个委托字段来支持您的事件:

public event EventHandler SomeEvent
{
    add { mSomeEvent += value; }
    remove { mSomeEvent -= value; }
}

private void RaiseSomeEvent()
{
    var handler = mSomeEvent;
    if( handler != null ) handler( this, EventArgs.Empty );
}

private EventHandler mSomeEvent;

请注意,如果您想引发事件,则必须引用支持字段。您不能再使用事件名称本身来执行此操作。您实际上可以为 INotifyPropertyChange 执行此操作,而无需求助于显式实现。

显式接口实现

当您显式实现接口时,您实际上创建了接口成员的“私有”版本。现在,我将 private 放在引号中,因为实现实际上并不是私有的。仅当从接口类型访问转换时才可访问该实现。有点啰嗦,举个例子:

public interface IFoo
{
    int Bar { get; }
}

public class A : IFoo
{
    int IFoo.Bar
    {
        get { return -1; }
    }
}

现在,假设我们在某个方法中有以下内容:

var a = new A();
int bar = a.Bar;

这将产生编译错误,因为类型 A 没有名为 Bar 的公开可见成员。但是,如果我们先转换为 IFoo

var a = new A();
int bar = ((IFoo) a).Bar;

现在可以编译,运行时 bar == -1。您也可以将变量 a 强类型化为 IFoo

IFoo a = new A();
int bar = a.Bar;

那也行。所以这个成员可以从 class 外部(甚至是程序集外部)访问,但是 只能 直接通过声明接口类型。这可用于隐藏您不想支持的实现(例如 IList<T> 的可变部分),或者如果您根据接口有不同的实现,例如 IEnumerable 中的 GetEnumerator()反对 IEnumerable<T>.

中的 GetEnumerator()
public class B : IFoo
{
    public int Bar { get { return 2; } }
    int IFoo.Bar { get { return 1; } }
}

现在如果我们像这样使用这个class:

B b = new B();
IFoo bAsIFoo = b;
int barFromB = b.Bar;
int barFromFoo = bAsIFoo.Bar;

您将在此处得到的是 barFromB == 2barFromFoo == 1