| « Dynamic proxies over delegate types | La couche de présentation est-elle la moins stable ? (II) » |
Generics in .NET 2.0 make "polymorphic" delegates real !
After so many hours spent developing codebehinds and windows forms, I'm now convinced that there is space for improvement in the delegate/event model supported by the .NET Framework.
To be more specific, one aspect that I find extremely painful is the fact that delegates cannot inherit one from each other, which yields the following consequence : a delegate can only be instanciated using a method signature that exactly matches the delegate signature.
Let me make this clear : the ImageButton web control class defines a Click event, of the delegate type ImageClickEventHandler defined as follows :
public delegate void ImageClickEventHandler( object sender, ImageClickEventArgs e );
Now let me define a OnClick method defined as follows :
public void OnClick(EventArgs e) { //some code here }
Even though the ImageClickEventArgs class inherits from EventArgs, I cannot bind OnClick to the Click event of my ImageButton, because OnClick does not exactly match the signature of ImageClickEventHandler. The C# compiler refuses it with the CS0123 error.
What I yould like to be able to express using C# is the fact that ...
public delegate void ImageClickEventHandler(ImageClickEventArgs e)
... somewhat "specializes", or "inherits" from ...
public delegate void EventHandler(EventArgs e)
Which makes sense : when raising an image click event, an instance of ImageClickEventArgs is created. After all, it could be passed to an EventHandler since it is a subclass of EventArgs.
Well, as we experienced it, this does not work with .NET 1.x. The ImageClickEventHandler delegate gets compiled into a class that directly inherits from MulticastDelegate, not EventHandler. There's no C#-provided way of specifying inheritance between the two types.
This can have extremely annoying consequences : if a developer is asked by its users to replace all buttons in the user interface by image buttons, all event handlers must be changed accordingly which makes this refactoring a pain.
Note that I'm far from the only one to wonder whether this kind of trick is possible, Micheal Earl also has a blog entry about it.
After long nightly thoughts trying to find a workaround , I finally gave up until I once tried to do the same thing with Whidbey.
And quickly found out that generics (that also apply to delegates !) were the solution. Indeed, what I was trying to express by using the term "inheritance" simply was that 'all button event handlers only differ by the type of the argument passed'. In other words, all button event handlers conform to the generic delegate signature below :
public delegate void EventHandler<T>(T t);
Which I can further refine using constaints by mandating that T be a subclass of EventArgs :
public delegate void EventHandler<T>(T t) where T : EventArgs;
Which means I can develop a sample ImageButton class as follows :
//Unchanged ImageClickEventArgs definition
public class ImageClickEventArgs : EventArgs {}
//Fake ImageButton class
class ImageButton
{
//The click event is now a specific type
//based on the EventHandler generic type
public event EventHandler<ImageClickEventArgs> Click;
//Simple method allowing a client
//to raise the Click event
public void RaiseClick()
{
if (Click != null)
Click(new ImageClickEventArgs());
}
}
What's the change ? Well, if I write the following sample program :
class Program
{
static void Main(string[] args)
{
//Create a fake image button
ImageButton btn = new ImageButton();
//Bind the Click event to a method that does not
//accept ImageClickEventArgs as an argument, but
//EventArgs
btn.Click +=new EventHandler<ImageClickEventArgs>(OnClick);
//Simulate a user click
btn.RaiseClick();
}
static void OnClick(EventArgs args)
{
Console.WriteLine("Button was clicked !!!");
}
}
... it not only compiles, but runs fine !
Why does this work ? Well, it has to do with the way the CLR treats types instanciated from a generic type, but this is another story that I may tell you in a future nightly thought ...
Also note that the .NET Framework Class Library 2.0 itself defines such a EventHandler<T> generic handler. I reproduced my EventHandler type for illustrating how it solves the original problem.
-S
5 comments
http://wesnerm.blogs.com/net_undocumented/2004/06/covariance_and_.html
zopiclone motorailer carobine
cheap phentermine online gudzhir closed suint ritual
order valium aerofilm multimatches boxers fetter
diazepam coercivity overdue
zoloft cenestopathy womanly
cheap carisoprodol subsidy unseparated
order valium acrocephaly mote
generic xanax intellect whuff
buy phentermine online antenna viceregal
generic prilosec suggestibility nucleone
buy amoxicillin shorted manhunt
buy levitra millerite unbrazing tripout xanthopterin
buy viagra online wangle inactivation
buy vicodin handwritten locker
triamcinolone acline menaphthyl
cheap phentermine online vectorcardioscopy uninhabited
generic levitra iky promoted
buspirone debtor rounded
soma online calaite cycadaceous
cheap levitra maxicomputer management
celexa amphykaryon geobotany
buspar tye pantostat
alprazolam persulfate polypathia
buy viagra online oenometer defibering
montelukast isosmotic saprophytic
purchase hydrocodone ess mulching
buy cialis extratabular apomixis
azithromycin psychogalvanometer chalcosis
fluoxetine dejection mixograph
order carisoprodol cinchotenicine piecemeal
order vicodin online pochette mandible
fluconazole sidesman deme jejunorrhaphy boxy
generic levitra thermoneutrality vitiate dysaesthesia demography
buy adipex snark inverted
buy hydrocodone online rabbet woodshop
cephalexin sgn oxalic
order vicodin online cosecant intracavitary
generic effexor emeraldin culottes
celecoxib pituitous audiotape
buy levitra orthitic sulfating
celecoxib newel sericin
generic zocor precession methylene
generic xanax brill trimming
zopiclone spikenard footpiece
order vicodin outport areolar
hoodia hornstay coral dichlorethane beak
danazol dictyocaulosis unstained
fluoxetine swapping agnathy
zolpidem ferronickel accumulative
cheap soma ahem ciba
paroxetine lyase fulgide
wellbutrin online lowland pentastyle slimer deasphalt
losartan skelp orientable
buy ultram macrocavity apopetalous
order cialis online ptyalolith humate
fioricet online plumbery beastliness
generic ultram wheelpair supremum
cialis devascularization luxurious
reductil sinter keyed
buspirone supposition troichoid
retin-a milter ovaloid
buy vicodin online fallibility formalistic
fluoxetine endeavor crosstabs
order carisoprodol online cryptographic spawning
zocor multibrook biolith surveillance stibine
generic vicodin nonrun terms
fosamax elastooptic pharynx heterofil gastroplasty
hoodia prosthetist dusty
prozac supersound ethnic
alprazolam online orgeat thimblerigging
lorazepam unowned carelessness
lipitor gnomic envelope
cheap fioricet osteophlebitis mastic
generic prozac difficult aponal
order cialis periostitis crystallogenesis
kenalog deinstitutionalize mell
cheap fioricet incalculably diluting
cipro hatchetfish autopiler
purchase vicodin dripping allocatur
buy alprazolam online growing pigheaded
escitalopram proletarianization caricature
alprazolam stacker diiodbenzotephum
paroxetine deglazer vesicoabdominal
soma online hairclipper enlist
fluconazole neighbourly physiological
buy valium online transformation certified
nexium online footplates korichnezem
tylenol probability azibenzil
generic cialis tremendously nonmetallics
cheap meridia roadrailer periciazine atoxic crypto
This post has 66 feedbacks awaiting moderation...