C # design defects (1): explicitly implementing events in the interface

Source: Internet
Author: User

In fact, after using C # for so many years, I may encounter some unpleasant design defects from time to time. These defects are mostly limited. Although they are harmless, they are easy to avoid. However, once these situations are met, they will always be unpleasant. After all, they are meaningless restrictions. Unfortunately, although removing these restrictions will not cause any problems, I don't think the C # design team will fix these problems. After all, they are mostly minor details. As a purebred code farmer using C #, I suddenly wanted to record these design defects on the rise and discuss them with everyone. This time we will start with implementing the events in the interface. When we need to explicitly implement an event in the interface, we will find that we must provide the add and remove accessors, this will slightly affect the commonly used event mode.
This problem may seem a bit difficult, but it is clear to read the code. For example, in a project, I will define such an INotifyPropertyChanged interface, which contains a PropertyChanged event:
Public interface INotifyPropertyChanged <TPropertyIdentity>
{
Event EventHandler <PropertyChangedEventArgs <TPropertyIdentity> PropertyChanged;
}

Public class PropertyChangedEventArgs <TPropertyIdentity>: EventArgs
{
Public PropertyChangedEventArgs (TPropertyIdentity propertyIdentity)
{
This. PropertyIdentity = propertyIdentity;
}

Public TPropertyIdentity PropertyIdentity {get; private set ;}
}
It can be seen that this interface is exactly the same as the built-in INotifyPropertyChanged event of. NET. In fact, they have the same purpose, that is, to notify the outside of a certain attribute of the object that has changed. The difference is that the system's built-in PropertyChangedEventArgs object uses the attribute name, that is, a string to identify an attribute, and in the above generic PropertyChangedEventArgs, you can use any type of objects to identify attributes, which undoubtedly brings more flexibility. For example, we can use continuous integer values to identify an object, so that we can use arrays to create an index, which has a higher performance than using a string as a key value dictionary.
However, when implementing the built-in INotifyPropertyChanged attribute of the system, we do not need to "use it by ourselves", but often want to notify other components, such as the ORM framework or UI control. Therefore, it is already a unified convention on the. NET platform. Even if it is insufficient, it cannot be discarded. Therefore, we often need to implement two INotifyPropertyChanged interfaces simultaneously on an object, for example:
Public class Item: INotifyPropertyChanged <int>, INotifyPropertyChanged
{
Public event EventHandler <PropertyChangedEventArgs <int> PropertyChanged;

Event PropertyChangedEventHandler INotifyPropertyChanged. PropertyChanged
{
Add {throw new NotImplementedException ();}
Remove {throw new NotImplementedException ();}
}
}
The above is the code framework automatically generated by Visual Studio for the two events and the second event. It requires us to provide the add and remove accessors. Why? I don't know. The C # development team may not be quite clear about the reason:
Interesting question. I did some poking around the language notes archive and I discovered that this demo-was made on the 13th of October, 1999, but the notes do not give a justification for the demo.
Off the top of my head I don't see any theoretical or practical reason why we cocould not have field-like explicitly implemented events. nor do I see any reason why we specifically ly need. this may have to remain one of the mysteries of the unknown.
Eric Lippert is a veteran C # team member. He often writes some C # design details on Stack Overflow or blog, it is a pity that even he thinks this is a "mystery ". In addition, "automatic attribute" makes this restriction even more "non-Expires header", because we can explicitly implement the attributes in the interface as follows:
Public interface INameProvider
{
String Name {get; set ;}
}

Public class MyNameProvider: INameProvider
{
String INameProvider. Name {get; set ;}
}
In this case, what is the essential difference between an event and it?
By the way, we know that explicitly implemented interface members cannot be labeled as abstract members in C #, which poses some additional problems for events. See the following code snippet:
Public abstract class Base: INotifyPropertyChanged <MyIdentity>
{
Public EventHandler <PropertyChangedEventArgs <MyIdentity> PropertyChanged;

Protected void OnPropertyChanged (PropertyChangedEventArgs <MyIdentity> args)
{
Var propertyChanged = this. PropertyChanged;
If (propertyChanged! = Null)
{
PropertyChanged (this, args );
}
}
}
Base is a Base class, so it often exposes an OnXyz method so that the subclass triggers the Xyz event. In the OnPropertyChanged method, we first determine whether _ propertyChanged is null, because null indicates that no one has registered the event-this is a common mode for using the event. If the event itself does not register any processor, it means that the event itself does not trigger, it also means that we can not even create EventArgs parameters required by the event. However, if we want to trigger an event in the subclass (that is, call the OnXxx method), there is no way to check whether the event has a processor registered. If this EventArgs object is created at a high cost, it will cause a certain performance loss.
The solution is also simple. For example, add an event to the base class:
Public abstract class Base: INotifyPropertyChanged <MyIdentity>
{
Public abstract event EventHandler <PropertyChangedEventArgs <MyIdentity> MyIdentityPropertyChanged;

Event EventHandler <PropertyChangedEventArgs <MyIdentity> INotifyPropertyChanged <MyIdentity>. PropertyChanged
{
Add {this. MyIdentityPropertyChanged + = value ;}
Remove {this. MyIdentityPropertyChanged-= value ;}
}
}
Or simply add a "delay" to construct the overload of EventArgs:
Public abstract class Base: INotifyPropertyChanged <MyIdentity>
{
Private EventHandler <PropertyChangedEventArgs <MyIdentity> _ propertyChanged;

Event EventHandler <PropertyChangedEventArgs <MyIdentity> INotifyPropertyChanged <MyIdentity>. PropertyChanged
{
Add {this. _ propertyChanged + = value ;}
Remove {this. _ propertyChanged-= value ;}
}

Protected void OnPropertyChanged (PropertyChangedEventArgs <MyIdentity> args ){...}

Protected void OnPropertyChanged (Func <PropertyChangedEventArgs <MyIdentity> argsFactory ){...}
}
When an event is triggered in the base class:
This. OnPropertyChanged () => new PropertyChangedEventArgs <MyIdentity> (new MyIdentity ()));
If you think it is a waste to create a delegate object without an event processor, you can solve it yourself. There is no difficulty, and it should not be hard to think out.

 


Author zhaojie

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.