Why aren't .NET web control event handlers generic? - c#

I've been wondering this for a while now; but especially more so since I've been more focused on front-end development for the last few weeks. It might sound like a broad question, but hopefully there's an answer, or a reason as to:
Why aren't .NET web control event handlers generic?
Reasoning
The reason I ask, is due to the nicety and elegance of strongly typed event handlers. Throughout my project, wherever required, I tend to use the .NET generic EventHandler<T> delegate, which has been around since .NET 2.0; as discussed here.
public delegate void EventHandler<TArgs>(object sender, TArgs args) where TArgs : EventArgs
It would be relatively straight forward to expand on this, and to define a type for the sender as well, something like so.
public delegate void EventHandler<TSender, TArgs>(TSender sender, TArgs args) where TArgs : EventArgs
Whenever working with .NET controls, occassionally I find myself binding the event handler in the code-behind rather than the ASPX file, and then having to cast the object to the desired type if I need to do any additional checks or alterations.
Existing
Definition
public class Button : WebControl, IButtonControl, IPostBackEventHandler
{
public event EventHandler Click;
}
Implementation
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.MyButton.Click += new EventHandler(MyButton_Click);
}
protected void MyButton_Click(object sender, EventArgs e)
{
// type cast and do whatever we need to do...
Button myButton = sender as Button;
}
Generic
Definition
public class Button : WebControl, IButtonControl, IPostBackEventHandler
{
public event EventHandler<Button, EventArgs> Click;
}
Implementation
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.MyButton.Click += new EventHandler(MyButton_Click);
}
protected void MyButton_Click(Button sender, EventArgs e)
{
// no need to type cast, yay!
}
I know it's a relatively small change, but surely it's more elegant? :)

Because it's old.
The web controls were developed for .NET 1.0, and generics didn't arrive until .NET 2.0.
Of course the controls could have been changed, but that means that all old code would need to be changed to compile (and they would need to be recompiled as the old binaries wouldn't work any more), and all old examples (millions of web pages) would be obsolete.

Related

Cannot assign event handler that uses derived type for EventArgs in c#

So I have an event declared as follows:
public event EventHandler OnChangeDetected;
Then I have the following handler that gets assigned to that event.
myObject.OnChangeDetected += OnTableChanged;
My understanding is that this type of event would require that my OnTableChanged method to have the following signature which compiles just fine.
public void OnTableChanged(object sender, EventArgs e)
Now I want to replace the OnTableChanged event to the following signature.
public void OnTableChanged(SqlChangeNotifier sender, SqlNotificationEventArgs e)
However, when I replace the parameters with derived types, it complains that there is no overload for "OnTableChanged" that matches delegate EventHandler. Since SqlChangeNotifier derives from Object, and SqlNotificationEventArgs derives from EventArgs, can anyone explain why I cannot have these derived parameters types since they inherit from the correct base types?
EventHandler is a delegate of the type void EventHandler(object sender, EventArgs e). So the signature of the handlers that subscribe to the event has to match this.
Now void OnTableChanged(SqlChangeNotifier sender, SqlNotificationEventArgs e) is more specific than that: It can no longer take any sender object and the event arguments also have to be of type SqlNotificationEventArgs.
The problem is now that when an event is raised, the original sender of the event will try to call the event handlers with the arguments object sender, EventArgs e but your method requires more specialized types. There is no guarantee from the type system that these arguments are in fact of those specialized types.
If you want to require these types, you will need to change the type of your event to a more restrictive delegate type.
You can't do that because the subscriber of the event should get a derived instance, but the publisher can only provide a base instance.
You are allowed to do the opposite though:
public static event KeyEventHandler ChangeDetected; // handler with derived args signature
private static void Program_ChangeDetected(object sender, EventArgs e) // base event args are OK
{
Console.WriteLine("ChangeDetected");
}
static void Main(string[] args)
{
ChangeDetected += Program_ChangeDetected;
ChangeDetected?.Invoke(null, new KeyEventArgs(default)); // base event args are NOT OK
}
For the overall topic on this sort of change, see Contravariance and Covariance.
Keep in mind that event systems are just fancy ways of calling a series of method which you can swap out at runtime, so if you can't call the handler directly with the exact same arguments you pass to raise the event, then the event system can't do it either.
Like methods, event handlers are Contravariant, meaning that a delagate of type EventHandler<SpecializedEventArgs> (assuming SpecializedEventArgs : EventArgs) will accept a handler of signature public void Handler(object sender, EventArgs args) because the call to the event ends up calling the handler with a SpecializedEventArgs object, which can be implicitly converted to EventArgs by simple polymorphism. IE the following will compile:
public static event EventHandler<SpecializedEventArgs> Event;
public static void Handler(object sender, EventArgs args) { }
public static void Main() {
Event += Handler;
//...

For which purpose the events pattern declares the event firerer method as virtual?

I am trying to understand for which purpose the events pattern decided that the method that fires the event should be declared virtual.
From C#6 in a Nutshell, from Joseph and Ben Albahari, O'Reilley:
Finally, the pattern requires that you write a protected virtual method that fires the
event. The name must match the name of the event, prefixed with the word On, and
then accept a single EventArgs argument:
Below a snippet I created to try to investigate.
I had the impression that the idea was to allow inheriting classes to completely overwrite how the event is handled, out of the box (original class). But the snippet below shows this is not possible, because deriving classes will never be able to invoke the event objects themselves (by the one one of the goals of the constraints imposed by the keyword event on delegates). The event can be invoked only in the containing class.
Now, since the pattern also asks that the method that fires the event simply check if the the event is not null and then call the delegate, with whatever every subscriber asked to do, what is left to be achieved by having the method that fires the event as virtual ? Inheriting classes are obliged to invoke the event the way it is in the broadcaster class, so all that is left for them is to add functionality. But this is exactly what they can achieve by subscribing to the event, in other words, by adding a call to an external function the time the event is fired.
I hope my wording is clear enough.
namespace eventsPatternVirtualEventFirerer
{
internal class Program
{
private static void Main(string[] args)
{
var obj = new X();
obj.ev += Obj_ev;
obj.Start();
}
private static void Obj_ev(object sender, EventArgs e)
{
Console.WriteLine("subscriber code...");
}
}
public class X
{
public event EventHandler<EventArgs> ev;
protected virtual void OnEvent(EventArgs e)
{
Console.WriteLine("original implementation...");
ev?.Invoke(this, e);
}
public void Start()
{
OnEvent(EventArgs.Empty);
}
}
public class X2 : X
{
public X2()
{
}
protected override void OnEvent(EventArgs e)
{
Console.WriteLine("inheriting class implementation overwrite...");
//compilation error - "the event 'X.ev' can only appear on the left hand side of += or -= (except when used from within the type 'X')"
ev?.Invoke(this, e);
}
}
}
I think the purpose is to allow derived classes to do something before/after the event is fired
public class X2 : X
{
public X2()
{
}
protected override void OnEvent(EventArgs e)
{
// Do something before the event
base.OnEvent(e);
// Do something after the event
}
}
There are a few things you can add/change in a derived class
Add a OnBeforeEvent / OnAfterEvent addition.
Choose not to broadcast the event (by conditionally not calling base.OnEvent(e)).
Vary the event args in some way.
Additionally, If you think about the way something like a page model works, it typically fires a Load event to notify when the page is loaded. Without a protected OnLoad method, derived classes would have to subscribe to it's own Load event to perform some action on load
public class MyPage : Page
{
protected override void OnLoad(EventArgs e)
{
// do something when the page is loaded
base.OnLoad(e);
}
}
versus:
public class MyPage : Page
{
public MyPage() : base()
{
this.Load += (sender,e) => {
// bleugh - subscribing to my own events
}
}
}
A good example might be the Paint event in Windows Forms.
// in MyButton : BaseButton : Control
void override OnPaint(object s, PaintEveargs e)
{
base.OnPaint(s, e); // Control: Draw background, BaseButton: draw border
// draw my own stuff
}
A button has several layers of base class, each drawing on top of each other.

call event with parameters from another event , both different namespace

I would like to call an event having parameters from another event, both have different namespaces, is it passable to do this?
public void event1(object1 sender, eventArgs e1)
{
// code goes here
}
public void event2(object2 sender, eventArgs e2)
{
// here I want call event1 with parametersenter code here
}
is it possible?
The best way to do this is not in terms of events being called but just methods being called.
If in the simplest terms your events just called Method1 and Method2 respectively then you could easily have Method1 call Method2 and do whatever it does.
You could raise potentially raise event1 from inside event2 but it doesn't necessarily make sense to do so (for example if it is a click event it should only be raised by a click. It is best to just refactor common code into a method that can be easily called using standard OOP techniques. In my code below I've made the extracted methods static methods so they are easier to call from other objects and assumed that Method1 is in Class1 (not explicitly shown) which is in Namespace1. You may need to do things slightly differently if you need them to be non-static methods.
Here's the example of what I mean.
public void event1(object1 sender, eventArgs e1)
{
var myParameter = ...;
Method1(myParameter);
}
public static void Method1(object myParameter)
{
// code goes here
}
public void event2(object2 sender, eventArgs e2)
{
var myParameter = ...;
Method2(myParameter);
}
public static void Method2(object myParameter)
{
Namespace1.Class1.Method1(myParameter);
}
Using "MyNameSpace";
thats all you need.
I need to write 30 characters so
include "mynamespace" for c++.
Unless i misunderstood what you meant.

C# Passing Variables Between Methods or Eventhandlers

I am still trying to learn about c#, my question is how would I pass a variable from Item1_Click to Item2_Click, is this the same thing as passing them between methods or is this different because they are event handlers?
public partial class Events : System.Web.UI.UserControl
{
protected void Item1_Click(object sender, EventArgs e)
{
//code
}
protected void Item2_Click(object sender, EventArgs e)
{
//code
}
}
They are still regular methods, so you're still able to call them the same way you normally would.
protected void Item1_Click(object sender, EventArgs e)
{
// Call the event handler for Item2, passing the arguments
// Item1 received
Item2_Click(sender, e);
}
protected void Item2_Click(object sender, EventArgs e)
{
// Make it happen.
}
If you want to re-use Item1_Click just bind the click event of the other object to Item1_Click as well.
See the links below for some more information on events in C#.
MSDN Event Tutorial
MSDN C# Programming Guide (Events)
Event handlers are called by the publisher of the event. So you'd need to cache the value in a member variable if both handlers are in the same type. Item1 click caches something (e.g. the selection in a variable) and Item2 click uses this member variable for its own handling.
However nothing stops you from calling the event-handler#2 from event-handler#1 ; since it is a method after all. In this case, you could slot in the parameter in the EventHandler argument but it is a bit non-intuitive.
What you've shown above, is a method. It's just that I imagine you've subscribed your methods to events on a couple of buttons.
It is then up to the buttons to populate the EventArgs instances themselves. If you wish to alter what goes into an EventArgs then you'd need to inherit from a Button and override the OnClick method to fire the event manually.
You could have some state information on your form (if you want shared information between the two methods). Or if you're literally wanting to pass information from Item1_Click to Item2_click then you can just call:
protected void Item1_Click(object sender, EventArgs e)
{
this.Item2_Click(sender new EventArgs()); // <== Stick information in EventArgs
}
If you want to preserve some value from a first click you can set a variable and read it from your other handler.
public partial class Events : System.Web.UI.UserControl
{
private SomeType variable;
protected void Item1_Click(object sender, EventArgs e)
{
variable = someValue;
//code
}
protected void Item2_Click(object sender, EventArgs e)
{
//code + do stuff with variable
}
}
Event handlers are methods themselves, so no difference there. Sending data (variables if you will) between methods is done through parameters, however, Event handlers are required to have a specific signature, so you can't just add more parameters. The way to go here is to use a class member (field or property) as some sort of "global variable" (global to the class) as mentioned in #Zebi's answer
Hope this helps :)
An event handler is just a method, that is called in some specific scenario. There's nothing to prevent you from explicitlly calling those methods, so
protected void Item1_Click(object sender, EventArgs e)
{
Item2_Click(sender, e); //pass the original arguments
Item2_Click(null, null); //pass some other arguments
Item1_Click(null, null); //recursively call the handler.
}
is perfectly valid C# code. However, it's a bad practice to use event handler for anything else than, basically handling the event. If two handlers need to use some same logic, it's better to do:
protected void Item1_Click(object sender, EventArgs e)
{
CommonLogic();
}
protected void Item2_Click(object sender, EventArgs e)
{
CommonLogic();
}
private void CommonLogic()
{
//the common logic goes here
}
Instead of calling your event handler directly, you might want to create methods that wrap the functionality of Item1_Click and Item2_Click. For example..
public partial class Events : System.Web.UI.UserControl
{
protected void Item1_Click(object sender, EventArgs e)
{
var theParam = GetParamValue();
//code
Item1Method(theParam);
}
protected void Item2_Click(object sender, EventArgs e)
{
Item2Method();
}
private void Item1Method(object param1)
{
// Item 1 code goes here...
}
private void Item2Method()
{
//item 2 code goes here...
// then call Item1Method
var param2 = GetParamValue();
Item1Method(param2);
}
}
This is just an example of how to avoid calling your event handlers. Doing this will make your code more maintainable down the road.
In addition, now you don't have to worry about providing a sender and Event Args as parameters when trying to run the functionality in Item1_Click

Using lambda expressions for event handlers

I currently have a page which is declared as follows:
public partial class MyPage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
//snip
MyButton.Click += (o, i) =>
{
//snip
}
}
}
I've only recently moved to .NET 3.5 from 1.1, so I'm used to writing event handlers outside of the Page_Load. My question is; are there any performance drawbacks or pitfalls I should watch out for when using the lambda method for this? I prefer it, as it's certainly more concise, but I do not want to sacrifice performance to use it. Thanks.
There are no performance implications since the compiler will translate your lambda expression into an equivalent delegate. Lambda expressions are nothing more than a language feature that the compiler translates into the exact same code that you are used to working with.
The compiler will convert the code you have to something like this:
public partial class MyPage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
//snip
MyButton.Click += new EventHandler(delegate (Object o, EventArgs a)
{
//snip
});
}
}
Performance-wise it's the same as a named method. The big problem is when you do the following:
MyButton.Click -= (o, i) =>
{
//snip
}
It will probably try to remove a different lambda, leaving the original one there. So the lesson is that it's fine unless you also want to be able to remove the handler.
EventHandler handler = (s, e) => MessageBox.Show("Woho");
button.Click += handler;
button.Click -= handler;
No performance implications that I'm aware of or have ever run into, as far as I know its just "syntactic sugar" and compiles down to the same thing as using delegate syntax, etc.

Categories