实现INotifyPropertyChanged的差异

时间:2015-03-08 22:57:56

标签: c# wpf events

我正在学习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生成的代码,那么与第一个示例相比,它会如何?

2 个答案:

答案 0 :(得分:2)

我建议阅读.NET中的Event和EventHandlers。

让我们称之为

public event PropertyChangedEventHandler PropertyChanged;

和这个B

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

相似,但存在差异。

  1. PropertyChanged是显式的(您已为其指定了访问修饰符)public,其中B PropertyChanged在为公共接口提供实现时隐式公开。
  2. PropertyChanged实现默认add/remove,它们只是PropertyChanged += handlerProeprtyChanged -= handler的包装,其中B PropertyChanged实现add/remove为{ {1}}。因此,如果要尝试订阅B'PropertyChanged'事件,它将抛出throw new exception
  3. 使用您的班级或您的班级NotImplementedException的任何人都可以访问PropertyChanged个活动,其中B INotifyPropertyChanged活动可以访问一次有人将你的课程强加给PropertyChanged
  4. 现在你的另一段代码:

    INotifyPropertyChanged

    用于调用public void NotifyPropertyChanged(string propertyName) { if (this.PropertyChanged != null) { this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName) } } 事件(如果不为null),并通知任何订阅者PropertyChanged已更改。每当您想要从外部代码调用类上的事件时,您都需要这样做,因为事件可以从您的班级中调用 ONLY

答案 1 :(得分:2)

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

添加和删除事件

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

如果您愿意,可以自己创建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执行此操作,而无需借助显式实现。

显式接口实现

当您明确实现接口时,实际上创建了接口成员的“私有”版本。现在,我将私有引用,因为实现实际上并不是私有的。只有从界面类型访问强制转换时,才能访问该实现。这有点拗口,所以这是一个例子:

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;

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

public class B : IFoo
{
    public int Bar { get { return 2; } }
    int IFoo.Bar { get { return 1; } }
}

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

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

您将获得barFromB == 2barFromFoo == 1