实现 INotifyPropertyChanged 的差异
本文关键字:INotifyPropertyChanged 实现 | 更新日期: 2024-10-31 16:12:45
我正在学习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 生成的代码,与第一个示例相比会如何?
这里确实有两个问题。 首先,事件中的添加和删除是怎么回事,其次,什么是显式接口实现? 这些是正交的东西。
具有添加和删除功能的事件
在 C# 中,事件实际上很像属性。 事件具有add
方法和remove
方法。 如果未显式指定它们,编译器将为您创建它们以及一个委托字段来支持事件(您可以通过引用事件从您自己的类中访问该字段)。
如果您愿意,可以自己创建add
和remove
方法(如果要显式实现接口,则必须这样做)。 在这种情况下,您通常还会创建一个委托字段来支持您的事件:
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;
那也行得通。 因此,可以从类外部(甚至程序集外部)访问此成员,但只能直接通过声明接口类型访问。 这对于隐藏您不想支持的实现(例如IList<T>
的可变部分)或根据接口有不同的实现(例如IEnumerable
中的GetEnumerator()
而不是IEnumerable<T>
中的GetEnumerator()
)非常有用。
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 == 2
和barFromFoo == 1
。
我建议阅读.NET中的事件和事件处理程序。
我们称之为 A
public event PropertyChangedEventHandler PropertyChanged;
和这个 B
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
{
add { throw new NotImplementedException(); }
remove { throw new NotImplementedException(); }
}
相似,但也有不同。
PropertyChanged
是显式的(您已经为其指定了访问修饰符),而 BPropertyChanged
是隐式公共的,因为您正在为公共接口提供实现。- A
PropertyChanged
实现了默认的add/remove
,它只是PropertyChanged += handler
和ProeprtyChanged -= handler
的包装器,其中 BPropertyChanged
实现add/remove
作为throw new exception
。因此,如果有任何内容试图订阅 B 'PropertyChanged' 事件,它将抛出NotImplementedException
。 - 任何使用您的班级或您的班级作为
INotifyPropertyChanged
的班级的人都可以访问PropertyChanged
事件,而作为 BPropertyChanged
事件只有在有人将您的类投射到INotifyPropertyChanged
时才能访问。
现在你的另一段代码:
public void NotifyPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)
}
}
用于调用 PropertyChanged
事件(如果不是 null),并通知任何订阅者给予者propertyName
已更改。每当你想要从外部代码调用类上的事件时,你都需要,因为事件只能从你的类中调用。