我正在学习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生成的代码,那么与第一个示例相比,它会如何?
答案 0 :(得分:2)
我建议阅读.NET中的Event和EventHandlers。
让我们称之为
public event PropertyChangedEventHandler PropertyChanged;
和这个B
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
{
add { throw new NotImplementedException(); }
remove { throw new NotImplementedException(); }
}
相似,但存在差异。
PropertyChanged
是显式的(您已为其指定了访问修饰符)public,其中B PropertyChanged
在为公共接口提供实现时隐式公开。PropertyChanged
实现默认add/remove
,它们只是PropertyChanged += handler
和ProeprtyChanged -= handler
的包装,其中B PropertyChanged
实现add/remove
为{ {1}}。因此,如果要尝试订阅B'PropertyChanged'事件,它将抛出throw new exception
。NotImplementedException
的任何人都可以访问PropertyChanged
个活动,其中B INotifyPropertyChanged
活动仅可以访问一次有人将你的课程强加给PropertyChanged
。现在你的另一段代码:
INotifyPropertyChanged
用于调用public void NotifyPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)
}
}
事件(如果不为null),并通知任何订阅者PropertyChanged
已更改。每当您想要从外部代码调用类上的事件时,您都需要这样做,因为事件可以从您的班级中调用 ONLY 。
答案 1 :(得分:2)
这里真的有两个问题。首先,在事件中添加和删除是什么,第二,什么是显式接口实现?这些是正交的东西。
在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
执行此操作,而无需借助显式实现。
当您明确实现接口时,实际上创建了接口成员的“私有”版本。现在,我将私有引用,因为实现实际上并不是私有的。只有从界面类型访问强制转换时,才能访问该实现。这有点拗口,所以这是一个例子:
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()
中的IEnumerable
与GetEnumerator()
中的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 == 2
和barFromFoo == 1
。