EventHandler with Task as return value - c#

The base C# EventHandler is defined as:
namespace System
{
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
}
Does anyone if there is an awaitable event handler available? E.g.
public delegate Task EventHandlerAsnyc<TEventArgs>(object sender, TEventArgs e);
Thx

If you want your event to be processed async (meaning you can use await to return early and resume later) you can simply declare the handler as async void:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponents();
myButton.Click += myButton_Click;
}
public async void myButton_Click(object sender, EventArgs e)
{
myButton.Enabled = false;
await SomeAsyncOrLongRunningOnAnotherThreadTask();
myButton.Enabled = true;
}
}
This way SomeAsyncOrLongRunningOnAnotherThreadTask() won't block your UI thread. And the handler is resumed after that task completes.
Side note: normally async methods should always return a Task or Task<T> that can be awaited or otherwise handled by the caller. The use case above is (afaik) the only justified case where void should be used for an async method.

The downside of just using async void handlers is that there is no way for the caller to wait for the result. This may be an issue for some interactive event handlers, like the ones using CancelEventArgs.
But you can still declare a Task-returning delegate type, if you wish. You just have to be careful how you raise it then. For instance, you could make an extension method which you can call as handler.Raise(sender, EventArgs.Empty).
public delegate Task EventHandlerAsnyc<TEventArgs>(object sender, TEventArgs eventArgs);
public static async Task Raise<TEventArgs>(this EventHandlerAsnyc<TEventArgs> handlers, object sender, TEventArgs eventArgs)
{
if (handlers == null)
return;
foreach (var handler in handlers.GetInvocationList())
await ((EventHandlerAsnyc<TEventArgs>)handler).Invoke(sender, eventArgs);
}
Alternatively, you can allow handlers to execute concurrently. But this would probably be a bad idea unless well-documented, as it's probably considered surprising behavior.
public static Task RaiseAllowConcurrent<TEventArgs>(this EventHandlerAsnyc<TEventArgs> handlers, object sender, TEventArgs eventArgs)
{
if (handlers == null)
return Task.CompletedTask;
var invocationList = handlers.GetInvocationList();
var tasks = new Task[invocationList.Length];
for (var i = 0; i < invocationList.Length; ++i)
tasks[i] = ((EventHandlerAsnyc<TEventArgs>)invocationList[i]).Invoke(sender, eventArgs);
return Task.WhenAll(tasks);
}

Related

await and event handler

It's permitted to convert and usual event handler from void to Task based and await it like below?
Something.PropertyChanged += async (o, args) => await IsButtonVisible_PropertyChanged(o, args);
Something.PropertyChanged -= async (o, args) => await IsButtonVisible_PropertyChanged(o, args);
private Task IsButtonVisible_PropertyChanged(object sender,PropertyChangedEventArgs e)
{
if (IsSomthingEnabled)
{
return SomeService.ExecuteAsync(...);
}
return Task.CompletedTask;
}
Or do it like this?
Something.PropertyChanged += IsButtonVisible_PropertyChanged;
Something.PropertyChanged -= IsButtonVisible_PropertyChanged;
private void IsButtonVisible_PropertyChanged(object sender,PropertyChangedEventArgs e)
{
if (IsSomthingEnabled)
{
_ = SomeService.ExecuteAsync(...);
}
}
Update:
Or this one, I know that the use Task void It should be banned, because exception it's not catched, but maybe for the case of an Eventhandler it's ok since the Eventhandler doesn't return.
Something.PropertyChanged += IsButtonVisible_PropertyChanged;
Something.PropertyChanged -= IsButtonVisible_PropertyChanged;
private async void IsButtonVisible_PropertyChanged(object sender,PropertyChangedEventArgs e)
{
if (IsSomthingEnabled)
{
await = SomeService.ExecuteAsync(...);
}
}
The syntax for asynchronous event handlers is :
Something.PropertyChanged += IsButtonVisible_PropertyChanged;
...
private async void IsButtonVisible_PropertyChanged(object sender,
PropertyChangedEventArgs e)
{
if (IsSomethingEnabled)
{
await SomeService.ExecuteAsync(...);
}
}
This allows awaiting asynchronous operations inside the event handler without blocking the UI thread. This can't be used to await for an event in some other method though.
Awaiting a single event
If you want some other code to await for an event to complete you need a TaskCompletionSource. This is explained in Tasks and the Event-based Asynchronous Pattern (EAP).
public Task<string> OnPropChangeAsync(Something x)
{
var options=TaskCreationOptions.RunContinuationsAsynchronously;
var tcs = new TaskCompletionSource<string>(options);
x.OnPropertyChanged += onChanged;
return tcs.Task;
void onChanged(object sender,PropertyChangedEventArgs e)
{
tcs.TrySetResult(e.PropertyName);
x.OnPropertyChanged -= onChanged;
}
}
....
async Task MyAsyncMethod()
{
var sth=new Something();
....
var propName=await OnPropertyChangeAsync(sth);
if (propName=="Enabled" && IsSomethingEnabled)
{
await SomeService.ExecuteAsync(...);
}
}
This differs from the example in two places:
The event handler delegate gets unregistered after the event fires. Otherwise the delegate would remain in memory as long as Something did.
TaskCreationOptions.RunContinuationsAsynchronously ensures that any continuations will run on a separate thread. The default is to run them on the same thread that sets the result
This method will await only a single event. Calling it in a loop will create a new TCS each time, which is wasteful.
Awaiting a stream of events
It wasn't possible to easily await multiple events until IAsyncEnumerable was introduced in C# 8. With IAsyncEnumerable<T> and Channel, it's possible to create a method that will send a stream of notifications :
public IAsyncEnumerable<string> OnPropChangeAsync(Something x,CancellationToken token)
{
var channel=Channel.CreateUnbounded<string>();
//Finish on cancellation
token.Register(()=>channel.Writer.TryComplete());
x.OnPropertyChanged += onChanged;
return channel.Reader.ReadAllAsync();
async void onChanged(object sender,PropertyChangedEventArgs e)
{
channel.Writer.SendAsync(e.PropertyName);
}
}
....
async Task MyAsyncMethod(CancellationToken token)
{
var sth=new Something();
....
await foreach(var prop in OnPropertyChangeAsync(sth),token)
{
if (propName=="Enabled" && IsSomethingEnabled)
{
await SomeService.ExecuteAsync(...);
}
}
}
In this case, only one event handler is needed. Every time an event occurs the property named is pushed to the Channel. Channel.Reader.ReadAllAsync() is used to return an IAsyncEnumerable<string> that can be used to loop asynchronously. The loop will keep running until the CancellationToken is signaled, in which case the writer will go into the Completed state and the IAsyncEnumerable<T> will terminate.
Quoting from Microsoft's article Async/Await - Best Practices in Asynchronous Programming, and specifically from the Avoid async void section:
Void-returning async methods have a specific purpose: to make asynchronous event handlers possible. [...] Event handlers naturally return void, so async methods return void so that you can have an asynchronous event handler.
Based on this, your third approach is the correct one:
private async void IsButtonVisible_PropertyChanged(object sender,
PropertyChangedEventArgs e)
{
if (IsSomethingEnabled)
{
await SomeService.ExecuteAsync();
}
}
Your first approach (+= async (o, args) => await) is technically equivalent, but it's not recommended because it is idiomatic and may cause confusion to future maintainers.
Your second approach (_ = SomeService.ExecuteAsync() launches the asynchronous operation in a fire-and-forget fashion, which is rarely a good idea because your application completely loses track of this task. It also elides async and await, which opens another can of worms.
the syntax for an async Event Handler is
async void handler(object sender,EventArgs args){}
and as Events don't return there is nothing to await for, so waiting for them is pointless
however if you need a response from an event then you can use the EventsArgs class to provide the response, eg
class FeedbackEventArgs:EventArgs
{
event EventHandler Completed;
Complete(){
this.Completed(this,EventArgs.Empty);
}
}
then you can use it as
event EventHandler<FeedbackEventArgs> myFeedbackEvent;
args = new FeedbackEventArgs();
args.Completed += OnCompleted;
this.myFeedbackEvent(this,args)
note if your handler is not async then you can assume that you code was paused while the event occurred, in which case you can just read a property from the eventArg rather than having to trigger an event
class FeedbackEventArgs:EventArgs
{
int result{get;set;}
}
event EventHandler<FeedbackEventArgs> myFeedbackEvent;
this.myFeedbackEvent(this,args)
args.result //this will be the result set in the sync handler
as noted by #Panagiotis this is a conceptual example not a working example

Unit testing async void event handler

I have implemented the MVP (MVC) pattern in c# winforms.
My View and Presenter are as follows (without all the MVP glue):
public interface IExampleView
{
event EventHandler<EventArgs> SaveClicked;
string Message {get; set; }
}
public partial class ExampleView : Form
{
public event EventHandler<EventArgs> SaveClicked;
string Message {
get { return txtMessage.Text; }
set { txtMessage.Text = value; }
}
private void btnSave_Click(object sender, EventArgs e)
{
if (SaveClicked != null) SaveClicked.Invoke(sender, e);
}
}
public class ExamplePresenter
{
public void OnLoad()
{
View.SaveClicked += View_SaveClicked;
}
private async void View_SaveClicked(object sender, EventArgs e)
{
await Task.Run(() =>
{
// Do save
});
View.Message = "Saved!"
}
I am using MSTest for unit testing, along with NSubstitute for mocking. I want to simulate a button click in the view to test the controller's View_SaveClicked code as have the following:
[TestMethod]
public void WhenSaveButtonClicked_ThenSaveMessageShouldBeShown()
{
// Arrange
// Act
View.SaveClicked += Raise.EventWith(new object(), new EventArgs());
// Assert
Assert.AreEqual("Saved!", View.Message);
}
I am able to raise the View.SaveClicked successfully using NSubstitute's Raise.EventWith. However, the problem is that code immediately proceeds to the Assert before the Presenter has had time to save the message and the Assert fails.
I understand why this is happening and have managed to get around it by adding a Thread.Sleep(500) before the Assert, but this is less than ideal. I could also update my view to call a presenter.Save() method instead, but I would like the View to be Presenter agnostic as much as possible.
So would like to know I can improve the unit test to either wait for the async View_SaveClicked to finish or change the View/Presenter code to allow them to be unit tested easier in this situation.
Any ideas?
Since you are just concerned about unit testing, then you can use a custom SynchronizationContext, which allows you to detect the completion of async void methods.
You can use my AsyncContext type for this:
[TestMethod]
public void WhenSaveButtonClicked_ThenSaveMessageShouldBeShown()
{
// Arrange
AsyncContext.Run(() =>
{
// Act
View.SaveClicked += Raise.EventWith(new object(), new EventArgs());
});
// Assert
Assert.AreEqual("Saved!", View.Message);
}
However, it's best to avoid async void in your own code (as I describe in an MSDN article on async best practices). I have a blog post specifically about a few approaches on "async event handlers".
One approach is to replace all EventHandler<T> events with plain delegates, and call it via await:
public Func<Object, EventArgs, Task> SaveClicked;
private void btnSave_Click(object sender, EventArgs e)
{
if (SaveClicked != null) await SaveClicked(sender, e);
}
This is less pretty if you want a real event, though:
public delegate Task AsyncEventHandler<T>(object sender, T e);
public event AsyncEventHandler<EventArgs> SaveClicked;
private void btnSave_Click(object sender, EventArgs e)
{
if (SaveClicked != null)
await Task.WhenAll(
SaveClicked.GetInvocationList().Cast<AsyncEventHandler<T>>
.Select(x => x(sender, e)));
}
With this approach, any synchronous event handlers would need to return Task.CompletedTask at the end of the handler.
Another approach is to extend the EventArgs with a "deferral". This is also not pretty, but is more idiomatic for asynchronous event handlers.
There must be a some type work being done of the running task, and you need to use something to return a value from the task.
Seems like the Thread.Sleep helps mitigate that, though, might help to add some logic, and get a value from the task.
From: https://msdn.microsoft.com/en-us/library/mt674882.aspx

Is it possible to have async methods as callbacks to eventhandlers in c#?

My design is illustrated by below example. Having a while true loop doing something and notifying by an event that it has done something to all subscribers. My application should not continue its execution before its done notifying all subscribers, where this works as long as someone do not put a async void on the callback.
If someone put a async void on the callback to await some task, then my loop can continue before the callback is completed. What other designs can I do to avoid this situation.
Its 3th party plugins that register themeself and subscribe to the event, so I have no control over if they put a async void. Understandable I cant do Task callbacks for the EventHandler, so what alternatives do I have with .net 4.5.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication4
{
public class Test
{
public event EventHandler Event;
public void DoneSomething()
{
if (Event != null)
Event(this,EventArgs.Empty);
}
}
class Program
{
static void Main(string[] args)
{
var test = new Test();
test.Event += test_Event;
test.Event +=test_Event2;
while(true)
{
test.DoneSomething();
Thread.Sleep(1000);
}
}
private static void test_Event2(object sender, EventArgs e)
{
Console.WriteLine("delegate 2");
}
static async void test_Event(object sender, EventArgs e)
{
Console.WriteLine("Del1gate 1");
await Task.Delay(5000);
Console.WriteLine("5000 ms later");
}
}
}
If someone put a async void on the callback to await some task, then my loop can continue before the callback is completed. What other designs can I do to avoid this situation.
There is really no way to avoid this. Even if you were to somehow "know" that the subscriber wasn't implemented via async/await, you still couldn't guarantee that the caller didn't build some form of asynchronous "operation" in place.
For example, a completely normal void method could put all of its work into a Task.Run call.
My application should not continue its execution before its done notifying all subscribers
Your current version does follow this contract. You're notifying the subscribers synchronously - if a subscriber does something asynchronously in response to that notification, that is something outside of your control.
Understandable I cant do Task callbacks for the EventHandler, so what alternatives do I have with .net 4.5.
Note that this is actually possible. For example, you can rewrite your above as:
public class Program
{
public static void Main()
{
var test = new Test();
test.Event += test_Event;
test.Event +=test_Event2;
test.DoneSomethingAsync().Wait();
}
}
public delegate Task CustomEvent(object sender, EventArgs e);
private static Task test_Event2(object sender, EventArgs e)
{
Console.WriteLine("delegate 2");
return Task.FromResult(false);
}
static async Task test_Event(object sender, EventArgs e)
{
Console.WriteLine("Del1gate 1");
await Task.Delay(5000);
Console.WriteLine("5000 ms later");
}
public class Test
{
public event CustomEvent Event;
public async Task DoneSomethingAsync()
{
var handler = this.Event;
if (handler != null)
{
var tasks = handler.GetInvocationList().Cast<CustomEvent>().Select(s => s(this, EventArgs.Empty));
await Task.WhenAll(tasks);
}
}
}
You can also rewrite this using event add/remove, as suggested by svick:
public class Test
{
private List<CustomEvent> events = new List<CustomEvent>();
public event CustomEvent Event
{
add { lock(events) events.Add(value); }
remove { lock(events) events.Remove(value); }
}
public async Task DoneSomething()
{
List<CustomEvent> handlers;
lock(events)
handlers = this.events.ToList(); // Cache this
var tasks = handlers.Select(s => s(this, EventArgs.Empty));
await Task.WhenAll(tasks);
}
}
My application should not continue its execution before its done notifying all subscribers, where this works as long as someone do not put a async void on the callback.
I have a blog entry on designing for async event handlers. It is possible to use Task-returning delegates or to wrap an existing SynchronizationContext within your own (which would allow you to detect and wait for async void handlers).
However, I recommend you use "deferrals", which are objects designed specifically to solve this problem for Windows Store applications. A simple DeferralManager is available in my AsyncEx library.
Your event args can define a GetDeferral method as such:
public class MyEventArgs : EventArgs
{
private readonly DeferralManager deferrals = new DeferralManager();
... // Your own constructors and properties.
public IDisposable GetDeferral()
{
return deferrals.GetDeferral();
}
internal Task WaitForDeferralsAsync()
{
return deferrals.SignalAndWaitAsync();
}
}
And you can raise an event and (asynchronously) wait for all asynchronous handlers to complete like this:
private Task RaiseMyEventAsync()
{
var handler = MyEvent;
if (handler == null)
return Task.FromResult<object>(null); // or TaskConstants.Completed
var args = new MyEventArgs(...);
handler(args);
return args.WaitForDeferralsAsync();
}
The benefit of the "deferral" pattern is that it is well-established in the Windows Store APIs, so it's likely to be recognized by end users.

Raising C# events with an extension method - is it bad?

We're all familiar with the horror that is C# event declaration. To ensure thread-safety, the standard is to write something like this:
public event EventHandler SomethingHappened;
protected virtual void OnSomethingHappened(EventArgs e)
{
var handler = SomethingHappened;
if (handler != null)
handler(this, e);
}
Recently in some other question on this board (which I can't find now), someone pointed out that extension methods could be used nicely in this scenario. Here's one way to do it:
static public class EventExtensions
{
static public void RaiseEvent(this EventHandler #event, object sender, EventArgs e)
{
var handler = #event;
if (handler != null)
handler(sender, e);
}
static public void RaiseEvent<T>(this EventHandler<T> #event, object sender, T e)
where T : EventArgs
{
var handler = #event;
if (handler != null)
handler(sender, e);
}
}
With these extension methods in place, all you need to declare and raise an event is something like this:
public event EventHandler SomethingHappened;
void SomeMethod()
{
this.SomethingHappened.RaiseEvent(this, EventArgs.Empty);
}
My question: Is this a good idea? Are we missing anything by not having the standard On method? (One thing I notice is that it doesn't work with events that have explicit add/remove code.)
It will still work with events that have an explicit add/remove - you just need to use the delegate variable (or however you've stored the delegate) instead of the event name.
However, there's an easier way to make it thread-safe - initialize it with a no-op handler:
public event EventHandler SomethingHappened = delegate {};
The performance hit of calling an extra delegate will be negligible, and it sure makes the code easier.
By the way, in your extension method you don't need an extra local variable - you could just do:
static public void RaiseEvent(this EventHandler #event, object sender, EventArgs e)
{
if (#event != null)
#event(sender, e);
}
static public void RaiseEvent<T>(this EventHandler<T> #event, object sender, T e)
where T : EventArgs
{
if (#event != null)
#event(sender, e);
}
Personally I wouldn't use a keyword as a parameter name, but it doesn't really change the calling side at all, so do what you want :)
EDIT: As for the "OnXXX" method: are you planning on your classes being derived from? In my view, most classes should be sealed. If you do, do you want those derived classes to be able to raise the event? If the answer to either of these questions is "no" then don't bother. If the answer to both is "yes" then do :)
Now C# 6 is here, there is a more compact, thread-safe way to fire an event:
SomethingHappened?.Invoke(this, e);
Invoke() is only called if delegates are registered for the event (i.e. it's not null), thanks to the null-conditional operator, "?".
The threading problem the "handler" code in the question sets out to solve is sidestepped here because, like in that code, SomethingHappened is only accessed once, so there is no possibility of it being set to null between test and invocation.
This answer is perhaps tangential to the original question, but very relevent for those looking for a simpler method to raise events.
[Here's a thought]
Just write the code once in the recommended way and be done with it. Then you won't confuse your colleagues looking over the code thinking you did something wrong?
[I read more posts trying to find ways around writing an event handler than I ever spend writing an event handler.]
Less code, more readable. Me like.
If you're not interested in performance you can declare your event like this to avoid the null check:
public event EventHandler SomethingHappened = delegate{};
You're not "ensuring" thread safety by assigning the handler to a local variable. Your method could still be interrupted after the assignment. If for example the class that used to listen for the event gets disposed during the interruption, you're calling a method in a disposed class.
You're saving yourself from a null reference exception, but there are easier ways to do that, as Jon Skeet and cristianlibardo pointed out in their answers.
Another thing is that for non-sealed classes, the OnFoo method should be virtual which I don't think is possible with extension methods.
To take the above answers a step further you could protect yourself against one of your handlers throwing an exception. If this were to happen then the subsequent handlers wouldn't be called.
Likewise, you could taskify the handlers to prevent a long-running handler from causing an excessive delay for the latter handlers to be informed. This can also protect the source thread from being hijacked by a long-running handler.
public static class EventHandlerExtensions
{
private static readonly log4net.ILog _log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public static void Taskify(this EventHandler theEvent, object sender, EventArgs args)
{
Invoke(theEvent, sender, args, true);
}
public static void Taskify<T>(this EventHandler<T> theEvent, object sender, T args)
{
Invoke(theEvent, sender, args, true);
}
public static void InvokeSafely(this EventHandler theEvent, object sender, EventArgs args)
{
Invoke(theEvent, sender, args, false);
}
public static void InvokeSafely<T>(this EventHandler<T> theEvent, object sender, T args)
{
Invoke(theEvent, sender, args, false);
}
private static void Invoke(this EventHandler theEvent, object sender, EventArgs args, bool taskify)
{
if (theEvent == null)
return;
foreach (EventHandler handler in theEvent.GetInvocationList())
{
var action = new Action(() =>
{
try
{
handler(sender, args);
}
catch (Exception ex)
{
_log.Error(ex);
}
});
if (taskify)
Task.Run(action);
else
action();
}
}
private static void Invoke<T>(this EventHandler<T> theEvent, object sender, T args, bool taskify)
{
if (theEvent == null)
return;
foreach (EventHandler<T> handler in theEvent.GetInvocationList())
{
var action = new Action(() =>
{
try
{
handler(sender, args);
}
catch (Exception ex)
{
_log.Error(ex);
}
});
if (taskify)
Task.Run(action);
else
action();
}
}
}

How do I make event callbacks into my win forms thread safe?

When you subscribe to an event on an object from within a form, you are essentially handing over control of your callback method to the event source. You have no idea whether that event source will choose to trigger the event on a different thread.
The problem is that when the callback is invoked, you cannot assume that you can make update controls on your form because sometimes those controls will throw an exception if the event callback was called on a thread different than the thread the form was run on.
To simplify Simon's code a bit, you could use the built in generic Action delegate. It saves peppering your code with a bunch of delegate types you don't really need. Also, in .NET 3.5 they added a params parameter to the Invoke method so you don't have to define a temporary array.
void SomethingHappened(object sender, EventArgs ea)
{
if (InvokeRequired)
{
Invoke(new Action<object, EventArgs>(SomethingHappened), sender, ea);
return;
}
textBox1.Text = "Something happened";
}
Here are the salient points:
You can't make UI control calls from a different thread than the one they were created on (the form's thread).
Delegate invocations (ie, event hooks) are triggered on the same thread as the object that is firing the event.
So, if you have a separate "engine" thread doing some work and have some UI watching for state changes which can be reflected in the UI (such as a progress bar or whatever), you have a problem. The engine fire's an object changed event which has been hooked by the Form. But the callback delegate that the Form registered with the engine gets called on the engine's thread… not on the Form's thread. And so you can't update any controls from that callback. Doh!
BeginInvoke comes to the rescue. Just use this simple coding model in all your callback methods and you can be sure that things are going to be okay:
private delegate void EventArgsDelegate(object sender, EventArgs ea);
void SomethingHappened(object sender, EventArgs ea)
{
//
// Make sure this callback is on the correct thread
//
if (this.InvokeRequired)
{
this.Invoke(new EventArgsDelegate(SomethingHappened), new object[] { sender, ea });
return;
}
//
// Do something with the event such as update a control
//
textBox1.Text = "Something happened";
}
It's quite simple really.
Use InvokeRequired to find out if this callback happened on the correct thread.
If not, then reinvoke the callback on the correct thread with the same parameters. You can reinvoke a method by using the Invoke (blocking) or BeginInvoke (non-blocking) methods.
The next time the function is called, InvokeRequired returns false because we are now on the correct thread and everybody is happy.
This is a very compact way of addressing this problem and making your Forms safe from multi-threaded event callbacks.
I use anonymous methods a lot in this scenario:
void SomethingHappened(object sender, EventArgs ea)
{
MethodInvoker del = delegate{ textBox1.Text = "Something happened"; };
InvokeRequired ? Invoke( del ) : del();
}
I'm a bit late to this topic, but you might want to take a look at the Event-Based Asynchronous Pattern. When implemented properly, it guarantees that events are always raised from the UI thread.
Here's a brief example that only allows one concurrent invocation; supporting multiple invocations/events requires a little bit more plumbing.
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public class MainForm : Form
{
private TypeWithAsync _type;
[STAThread()]
public static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MainForm());
}
public MainForm()
{
_type = new TypeWithAsync();
_type.DoSomethingCompleted += DoSomethingCompleted;
var panel = new FlowLayoutPanel() { Dock = DockStyle.Fill };
var btn = new Button() { Text = "Synchronous" };
btn.Click += SyncClick;
panel.Controls.Add(btn);
btn = new Button { Text = "Asynchronous" };
btn.Click += AsyncClick;
panel.Controls.Add(btn);
Controls.Add(panel);
}
private void SyncClick(object sender, EventArgs e)
{
int value = _type.DoSomething();
MessageBox.Show(string.Format("DoSomething() returned {0}.", value));
}
private void AsyncClick(object sender, EventArgs e)
{
_type.DoSomethingAsync();
}
private void DoSomethingCompleted(object sender, DoSomethingCompletedEventArgs e)
{
MessageBox.Show(string.Format("DoSomethingAsync() returned {0}.", e.Value));
}
}
class TypeWithAsync
{
private AsyncOperation _operation;
// synchronous version of method
public int DoSomething()
{
Thread.Sleep(5000);
return 27;
}
// async version of method
public void DoSomethingAsync()
{
if (_operation != null)
{
throw new InvalidOperationException("An async operation is already running.");
}
_operation = AsyncOperationManager.CreateOperation(null);
ThreadPool.QueueUserWorkItem(DoSomethingAsyncCore);
}
// wrapper used by async method to call sync version of method, matches WaitCallback so it
// can be queued by the thread pool
private void DoSomethingAsyncCore(object state)
{
int returnValue = DoSomething();
var e = new DoSomethingCompletedEventArgs(returnValue);
_operation.PostOperationCompleted(RaiseDoSomethingCompleted, e);
}
// wrapper used so async method can raise the event; matches SendOrPostCallback
private void RaiseDoSomethingCompleted(object args)
{
OnDoSomethingCompleted((DoSomethingCompletedEventArgs)args);
}
private void OnDoSomethingCompleted(DoSomethingCompletedEventArgs e)
{
var handler = DoSomethingCompleted;
if (handler != null) { handler(this, e); }
}
public EventHandler<DoSomethingCompletedEventArgs> DoSomethingCompleted;
}
public class DoSomethingCompletedEventArgs : EventArgs
{
private int _value;
public DoSomethingCompletedEventArgs(int value)
: base()
{
_value = value;
}
public int Value
{
get { return _value; }
}
}
}
As the lazy programmer, I have a very lazy method of doing this.
What I do is simply this.
private void DoInvoke(MethodInvoker del) {
if (InvokeRequired) {
Invoke(del);
} else {
del();
}
}
//example of how to call it
private void tUpdateLabel(ToolStripStatusLabel lbl, String val) {
DoInvoke(delegate { lbl.Text = val; });
}
You could inline the DoInvoke inside your function or hide it within separate function to do the dirty work for you.
Just keep in mind you can pass functions directly into the DoInvoke method.
private void directPass() {
DoInvoke(this.directInvoke);
}
private void directInvoke() {
textLabel.Text = "Directly passed.";
}
In many simple cases, you can use the MethodInvoker delegate and avoid the need to create your own delegate type.

Categories