简体   繁体   中英

Differences in Implementing INotifyPropertyChanged

I'm learning WPF and still trying to fully understand the INotifyPropertyChanged interface. I found the following example:

public event PropertyChangedEventHandler PropertyChanged;

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

The example follows the same format that I see often in other code. In Visual Studio 2013, I have the option of allowing the IDE to implement the interface for me explicitly. Doing so creates the following:

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

How are these two different? If I decided to use the code generated by Visual Studio, how would that look compared to the first example?

I would suggest reading up on Event and EventHandlers in .NET.

Lets call this A

public event PropertyChangedEventHandler PropertyChanged;

and this B

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

Are similar, but there are differences.

  1. A PropertyChanged is explicitly (you have specified the access modifier for it) public where as B PropertyChanged is implicitly public as you are providing an implementation for a public Interface.
  2. A PropertyChanged implements the default add/remove , which are just wrappers to PropertyChanged += handler and ProeprtyChanged -= handler where as B PropertyChanged implements add/remove as throw new exception . Therefore if anything was to try and subscribe to the B 'PropertyChanged' event, it will throw the NotImplementedException .
  3. A PropertyChanged event will be accesible to anyone using your class, or your class as INotifyPropertyChanged , where as B PropertyChanged event will ONLY be accessible once someone casts your class to INotifyPropertyChanged .

Now your other piece of code:

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

Is used to invoke the PropertyChanged event(if not null), and notify any subscribers that the giver propertyName has changed. You need whenever you want to invoke events on your class from external code, as events can ONLY be invoked from within your class.

There's really two questions here. First, what's with the add and remove in an event and second, what is an explicit interface implementation? These are orthogonal things.

Events with add and remove

In C#, events are actually a lot like properties. An event has an add method and a remove method. If you don't specify them explicitly, the compiler will create them for you as well as a delegate field to back the event (which you can access from within your own class by referring to the event).

You can create the add and remove methods yourself if you wish (and if you want to implement the interface explicitly, you must ). In that case you usually also create a delegate field to back your event:

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;

Notice that if you want to raise the event you have to refer to the backing field. You can no longer do it using the event name itself. You could actually do this for INotifyPropertyChange without resorting to an explicit implementation.

Explicit Interface Implementation

When you implement an interface explicitly you actually create "private" versions of the interface members. Now, I put private in quotes because the implementation is not actually private. The implementation is only accessible if the cast is accessed from the interface type. That's a bit of a mouthful, so here's an example:

public interface IFoo
{
    int Bar { get; }
}

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

Now, let's say we have the following in a method somewhere:

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

This will generate a compile error, because the type A doesn't have a publically visible member called Bar . However, if we cast to IFoo first:

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

Now it compiles and when it runs, bar == -1 . You could also have strongly typed the variable a as IFoo as well:

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

That would also work. So this member can be accessed from outside the class (and even outside the assembly), but only directly through the declaring interface type. This can be useful to hide implementation you don't want to support (like the mutable parts of IList<T> ) or if you have different implementations depending on the interface, like GetEnumerator() in IEnumerable as opposed to the GetEnumerator() in IEnumerable<T> .

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

Now if we use this class like so:

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

What you'll get here is barFromB == 2 and barFromFoo == 1 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM