I have a pretty basic event:
public event EventHandler OnAborted;
All I need to do is call this event, I don't even need to provide any arguments, so it's nothing fancy. I'm confused with the correct usage of the EventArgs argument.
I can use:
if (OnAborted != null)
OnAborted(this, EventArgs.Empty);
Or I can even use:
if (OnAborted != null)
OnAborted(this, new EventArgs());
In both cases, EventArgs seems to be pretty useless, I can't even provide any arguments (not that I need to, but that's not the point).
What is the proper usage of EventArgs? Should I create a custom class that inherits EventArgs?
Using EventArgs.Empty does not create a new object and allocates it on the heap. More over, EventArgs.Empty is an instance of the Null Object Pattern. Having an object representing "no value" to avoid checking for null when using it.
To add more to when you should use EventArgs or your proper class, here you have some MSDN guideline on Event Design :
Consider using a derived class of System.EventArgs as the event argument, unless you are absolutely sure the event will never need to carry any data to the event-handling method, in which case you can use the System.EventArgs type directly.
If you define an event that takes an EventArgs instance instead of a derived class that you define, you cannot add data to the event in later versions. For that reason, it is preferable to create an empty derived class of EventArgs. This allows you add data to the event in later versions without introducing breaking changes.
Related
I want to ask the different between EventHandler and EventHandler<T>.
Previously I have implemented a EventHandler, with a custom EventArgs which can be passed to the parent page from the user control.
I thought I need to apply EventHandler< T >, however it can be implemented by using EventHandler. (in fact there is weird error when I trying to apply EventHandler<T>, the program is worked but error is shown in the IDE which I couldn't solve [C# Custom EventHandler ])
Thus, I want to know in what situation I need to apply EventHandler < T > ?
public event EventHandler AppendProcess;
public event EventHandler<MyEventArg> AppendProcess;
---Update---
this is how I invoke the event in the user control (as i said, i can pass object to parent page by doing this (although i don't know if it is correct to do so)
if (AppendProcess == null) { }
else
AppendProcess(this, new Common.WinLose_ProgressStage(Common.WinLose_SP_Parameter.upper, displayLevel + 1,
(int)Common.WinLose_Level.lvChild4, thename, refundratio,
selfproportion, -1, -1, loadlevel, isPlayer, betsource, gamecategory, false));
EventHandler<T> is just a generic EventHandler type, which avoids you having to declare a new delegate type for each kind of EventArgs you want to use.
Consider Control.KeyPress for example. It's declared as an event of type KeyPressEventHandler. That delegate is just declared as:
public delegate void KeyPressEventHandler(object sender, KeyPressEventArgs e)
If EventHandler<T> (and generics) had been around when that was created, the event could have been declared as an EventHandler<KeyPressEventArgs> instead, saving a delegate declaration. There are many, many delegates which are just like EventHandler, and only vary in the type of the second parameter - EventHandler<T> avoids that redundancy.
No if you don't have your own custom EventArgs subclass, there's no reason to use EventHandler<T>... but if you do, it's much better to use it, so that the method handling the event receives your custom EventArgs subclass in a strongly-typed way.
As an aside, your way of invoking the event handler isn't thread-safe. Another thread could unsubscribe the final event handler after your nullity check. If you're using C# 5, you should write it as:
var handler = AppendProcess;
if (handler != null)
{
handler(this, new Common.WinLose_ProgressStage(...));
}
If you're using C# 6 or later, you can use the null conditional operator:
// If AppendProcess is null, the arguments won't even be evaluated
AppendProcess?.Invoke(this, new Common.WinLose_ProgressStage(...));
EventHandler<T> is the generic variant of EventHandler. Usually, you would override EventArgs and EventHandler to come up with an own event type. You still need to derive EventArgs if you want to pass in custom properties to the e argument, but you don't need to create a custom delegate for EventHandler any more. You now can just say:
public event EventHandler<SomeEventArgs> SomeEvent;
Which is more convenient than:
public delegate void SomeEventHandler(object sender, SomeEventArgs e);
public event SomeEventHandler SomeEvent;
When your delegate signature doesn't change, EventHandler<T> is much more straightforward to use.
From your other question:
Common.WinLose_ProgressStage wps = (Common.WinLose_ProgressStage)e;
That's a cast that could potentially fail, depending on how the method is invoked. There is nothing guaranteeing that your event handler only gets called with Common.WinLose_ProgressStage that can be inspected without checking your full program. It could be called with a plain EventArgs too: you made that possible by making the parameter type EventArgs.
EventHandler<T> causes more compile-time type checking. If other code attempts to pass in EventArgs.Empty, the call would simply fail to compile.
It's basically the same as "why shouldn't I make all my parameters of type object"? Sure, you could do that. And if you do that, casting back wherever needed, your code would work. But I know I prefer
long Add(long x, long y) { return x + y; }
over
object Add(object x, object y) { return (long)x + (long)y; }
and I suspect that in this case, you do too. Yet this is exactly what you're doing when you make the parameter type EventArgs rather than Common.WinLose_ProgressStage.
While playing around in VS 2015, i was creating a property and mistyped prior to hitting tab and it gave me what appears to be a very clean simple method for creating an event. I am unsure how to use this to accept additional parameters to pass it data (as I usually want to pass data when raising an event with information about what is in progress, or what was done)
The code it gave is as follows :
public event EventHandler<EventArgs> MyEvent;
protected virtual void OnMyEvent(EventArgs e)
{
EventHandler<EventArgs> handler = MyEvent;
if (handler != null)
handler(this, e);
}
At first glance it is similar to most examples however this method does not appear to require a delegate to operate. Compare with the following example from How to call an event manually in C#?
public delegate void MyEventHandler(object sender, MyEventArgs e)
public event MyEventHandler MyEvent;
public void RaisesMyEvent()
{
if(MyEvent != null) //required in C# to ensure a handler is attached
MyEvent(this, new MyEventArgs(/*any info you want handlers to have*/));
}
What I would like to know, in light of having more elegant (clean) code, is the method that microsoft provided (first method in this question) better (it seems cleaner), and if so, how could I adjust it slightly to accept a string in addition to standard event args and/or extend the eventargs to pass information ?
To pass custom arguments on an EventHandler, you would create your own class derived from EventArgs and change the declaration like so:
public event EventHandler<MyCustomEventArgs> MyEvent;
Alternatively, if you don't like having object sender in your handlers, you can do:
public event Action<MyCustomEventArgs> MyEvent;
or even
public event Action<string> MyEvent;
As far as the invocation, Microsoft's is better. The reason is that the null check you wrote isn't thread-safe. Storing off the handler before doing the null check ensures that you don't invoke a null method.
Alternatively, use C# 6 invocations and take advantage of the null conditional operator:
MyEvent?.Invoke(whateverArgs);
The code that Visual Studio auto-generated for you is actually better for one small condition:
public void RaisesMyEvent()
{
if(MyEvent != null) //required in C# to ensure a handler is attached
// Now if someone in another thread sets MyEvent to null the next line will still execute
MyEvent(this, new MyEventArgs(/*any info you want handlers to have*/));
// And throw a NullReferenceException
}
This code has a race condition. It's fairly rare to hit it, but it is possible. The Visual Studio boilerplate code avoids this race condition by making a local copy of the event handler before calling it.
As for how to pass more parameters to the event handler:
There are multiple ways, but the 'standard' way is to create an class that extends EventArgs. For instance, PaintEventArgs extends EventArgs and adds a Graphics and Rectangle properties for the OnPaint handler to use.
I'd follow that basic pattern by creating a MyEventArgs class based off EventArgs, and adding the properties your method will need to use.
Just replace EventArgs with your own class:
class MyEventArgs : EventArgs
{
public string Text { get; private set; }
public MyEventArgs(string text)
{
Text = text;
}
}
The EventHandler<T> generic delegate type will accept EventArgs or any subclass of EventArgs.
Then when you call OnMyEvent(), just pass an instance of your class with the data you want to deliver with the event.
I agree with the other notes regarding thread-safe invocation. This isn't always required, but it's simple enough to implement and the IDE-provided version is safe while yours is not.
We have a class which derives from FrameworkElement and which is not under our control.
This class registers eventhandlers in the OnInitialize method.
What is the pattern to properly clean this class up, since FrameworkElement does not provide a counterpart to OnInitialize?
It remains rooted since no part of it removes the EventHandler causing a leak.
There's no such thing as "deinitializing". Initialization methods are used when it isn't possible to fully initialize an object in its constructor, because it depends on data that isn't available during construction. In these cases construction is broken in two phases: the first step is executed in a parameterless constructor, the second in an initialization method, like OnInitialize, after the external data becomes available
What you describe is object disposal, which is performed by calling an object's Dispose method. A well-written class should clean up its data, release any external resources and release any event handlers.
Visual elements typically have another step in their lifecycle, handled by the OnLoad/OnUnload methods. The Load step occurs when an element is actually placed in XAML view and connected to the other UI elements. Event handlers should be registered in the OnLoad method and removed in the OnUnload method.
If the element has no Dispose method , you may be able to raise the Unload event to force the cleanup, although this is a bit of a hack:
control.RaiseEvent(new RoutedEventArgs(FrameworkElement.UloadedEvent))
Use weak event pattern. In particular, subscribe to events using WeakEventManager implementations (or, better, its generic version WeakEventManager<TEventSource, TEventArgs>, if .NET 4.5 is an option).
Use Dispatcher.ShutdownStarted event.
Add this to the constructor:
Dispatcher.ShutdownStarted += Dispatcher_ShutdownStarted;
And add your cleanup code to this method:
private void Dispatcher_ShutdownStarted(object sender, EventArgs e)
{
Stop();
}
I have a form with two RichTextBoxs on them, I want to raise an event in one to trigger an event on the form which updates something in the other rtb.
Where must I define the EventArgs extended class?
What is and where must my delegate be? Is this the function that is called from the event being raised?
Do these three classes (rtb1, rtb2, form1) need to be in the same namespace?
Rather than defining your own delegate you can almost always use the generic EventArgs class, or the Action class as the delegate for your events. When using one of these you won't need to define any delegate at all, they are already defined by the library.
The delegate is not the function being called, it merely defines the signature of all methods that are called by the event.
The three classes do not need to be in the same namespace, no. In fact, as a general rule, there are almost no situations (none that I can think of anyway) which would force you to have classes in the same namespace for them to do something.
When using the FormClosing event, why does the code e.Cancel = true; work, but new CancelEventArgs().Cancel = true; does not work?
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
new CancelEventArgs().Cancel = true;
}
The event is raised by the Winforms plumbing code. The only way it can see that the custom event handler wants to alter the default behavior is through the e object. Creating a new CancelEventArgs object has no side-effects that the plumbing can detect.
There's something else wrong, events are raised for the benefit of external code, letting it know what's going on and giving it an option to alter behavior. There is no external code here, the event handler is actually part of the same class that raises the event. In other words, the form is listening to its own events. There's a much better way to deal with that, you override the method that raises the event. Like this:
protected override void OnFormClosing(FormClosingEventArgs e) {
e.Cancel = true;
base.OnFormClosing(e);
}
Now external code can override the default behavior, events are raised after the OnXxxx method runs. And you have a choice, if you do not want the external code to override the behavior, simply swap the two statements.
I think the code is doing exactly what it says; what's missing is a literal reading of it.
When you assign a new value to e.Cancel, you are modifying the e that is provided as a parameter to the function. After the event handler function finishes, this FormClosingEventArgs instance, including any changes made to it from within the event handler, will be available to whatever code invoked the event handler. In this case, that's almost certainly the Winforms code written by Microsoft.
On the flip side, when you inside that event handler create a new instance of the type FormClosingEventArgs and do something to it, there is nothing to provide that information back to the caller; you'd need something explicit for that. Since the caller is looking at the value of the parameter it passed in once the event handler completes, you'd need to somehow replace the content of e as seen by the caller with the newly created instance. In other cases such a result might be provided as a return value.
In general, the result of new T(), for some type T, is an instance of type T. You can thus work with the result of the expression new T() as you would a non-null variable of type T. In your particular case, you're assigning a value to a property on type T (specifically, the instance of that type thus created). (There is the special case where the constructor fails, but let's not go there for now; for simple types, that would pretty much mean that you are in such dire straits that your program is unlikely to be able to continue running in any case.)
What's important here is that if you don't assign the result of the expression new T() itself anywhere, the newly created instance is thrown away (technically, becomes inaccessible) once the statement completes. Then at some later point, the .NET garbage collector kicks in and actually reclaims the memory that was allocated. It isn't really any different from allocating a variable in one function, calling that function from another function and trying to access the variable thus allocated from the second function without doing anything to transfer the variable from the first function to the second, except here only one function is involved.
Doing something like your second line of code in the event handler would be rather unusual, but can in principle be valid if invoking the constructor has some side effect that you're looking to make use of, such as triggering lazy loading.
This code surely work just check it
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
if (PreClosingConfirmation() == System.Windows.Forms.DialogResult.Yes)
{
Dispose(true);
Application.Exit();
}
else
{
e.Cancel = true;
}
}
private DialogResult PreClosingConfirmation()
{
DialogResult res = System.Windows.Forms.MessageBox.Show(" Do you want to quit? ", "Quit...", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
return res;
}
Happy Coding
It's because the CancelEventArgs is an object reference passed to the event handler in your code. The behind the scenes code raises FormClosing with a CancelEventArgs object and all the event handlers receive that same object. Once all the handlers have had their turn, the behind the scenes code checks the CancelEventArgs object it sent to see if its Cancel property is set to true. If it is, it doesn't do anything, and the FormClose chain of events stops. If Cancel is false (CancelEventArgs default value), meaning it hasn't been set to Cancel the chain of events, the behind the scenes code proceeds along, and then raises the FormClosed event.
You can read more at Form.FormClosing Event on MSDN.
In Forms all the -ing events are typically followed by an -ed event. The -ing events usually have CancelEventArgs, which can have its Cancel property set to true to stop the -ed event from happening.