This is a follow-up question to another SO question regarding the use of an async wrapper over an async callback function.
Here is the code as it stands (an excellent solution provided by #Servy):
static Task<ObservableCollection<MyResult>> GetMyDataAsync(Params p)
{
var tcs = new TaskCompletionSource<ObservableCollection<MyResult>>();
DoStuffClass stuff = new DoStuffClass();
stuff.LoadCompleted += (args) => tcs.TrySetResult(args.Result);
stuff.LongDrawOutProcessAsync(p);
return tcs.Task;
}
So, my problem is with the LoadCompleted event; here is the signature:
public event EventHandler<MyArgs> LoadCompleted;
MyArgs contains a property called ResultCollection; however, changing the code like this does not work:
stuff.LoadCompleted += (args) => tcs.TrySetResult(args.ResultCollection);
In fact, I get the error:
'System.EventHandler<MyArgs>' does not take 1 arguments
Which I can see if correct from the signature; so how can I set the LoadCompleted result to the TaskCompletionSource?
EventHandler needs 2 arguments, the first is the instance that raised the event and the second is the event arguments. You need to specify both of them even if you only use one (args).
This should work:
stuff.LoadCompleted += (sender, args) => tcs.TrySetResult(args.Result);
stuff.LoadCompleted += (sender, args) => tcs.TrySetResult(args.Result);
This should fix your problem
If you look at EventHandler<T> definition you will see it takes two arguments
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
So you need to pass two arguments in your assignment
stuff.LoadCompleted += (sender, args) => tcs.TrySetResult(args.Result);
Related
Before I start, the closest I could find for this here was How do you implement an async action delegate method? however, being a bear of a somewhat small brain, I couldn't get my braincells around the answer and how it might apply to my current issue.
I'm writing a wrapper around the RabbitMq C# Client, and am having a problem with my delegates and the async events.
This bit of code seems fine:
private Action<object, BasicAckEventArgs> _syncHandlerBasicAck;
private Action<object, BasicNackEventArgs> _syncHandlerBasicNack;
Defining the code here:
_syncHandlerBasicAck = delegate(object o, BasicAckEventArgs args)
{
//[Do stuff here]
};
_syncHandlerBasicNack = delegate(object o, BasicNackEventArgs args)
{
//[Do stuff here]
};
then using it with:
Channel.BasicAcks += _syncHandlerBasicAck.Invoke;
Channel.BasicNacks += _syncHandlerBasicNack.Invoke;
The issue I have is with the async BasicDeliver event. This is my current code:
private Func<object, BasicDeliverEventArgs, Task> _syncHandlerIncomingMessage;
followed by
_syncHandlerIncomingMessage = async delegate(object o, BasicDeliverEventArgs args)
{
//[Do stuff here]
};
however whenever I try to do:
Consumer.Received += _syncHandlerIncomingMessage.Invoke;
I get a System.NullReferenceException telling me that
Object reference not set to an instance of an object.
I see that there's a BeginInvoke method available but I'm not sure that applies to my current situation since I'm not using Callbacks and just want to call this asynchronously.
Anyone got any pointers? (I'll even take "Duplicate question: Answer here" type responses as I'm sure I may have missed something).
Edit
I had two issues; one with wiring this up, the other where some refactoring got in my way! Thanks so much to #Coolbots for the answer and #Camilo to show me the error of my ways.
The problem is event subscriptions do not support async delegate directly, so a wrapper is needed, like so:
Alternative syntax (see EDIT):
async Task MyDelegate(object sender, BasicDeliverEventArgs e)
{
// Do stuff
}
Consumer.Received += async (s, e) => await MyDelegate(s, e);
I'm on my phone (can't test code), so while I'm certain this works with an async Task MyDelegate(...), I'm not 100% this will work with async delegate MyDelegate(...); although, in theory, it should.
EDIT: Deleted, then restored my answer per OP's request (I'm glad it was of some value); finally got to a computer, and verified that all 3 subscription approaches work, as verified by #CamiloTerevinto in the commments, and reason for my original deletion of this answer:
Func<object, BasicDeliverEventArgs, Task> _syncHandlerIncomingMessage;
_syncHandlerIncomingMessage = async delegate (object sender, BasicDeliverEventArgs eventArgs)
{
await Task.Delay(1000); // Do stuff
};
// OR
async Task SyncHandlerIncomingMessage(object sender, BasicDeliverEventArgs eventArgs)
{
await Task.Delay(1000); // Do stuff
}
// Can be subscribed as follows:
Consumer.Received += _syncHandlerIncomingMessage.Invoke; // as posted by OP
// OR
Consumer.Received += async (s, e) => await SyncHanlderIncomingMessage.Invoke(s, e);
// OR
Consumer.Received += SyncHandlerIncomingMessage;
In summary: no wrapper necessary. I am glad my original answer was of value to the OP; I am adding this edit for completeness/correctness of the answer, in hopes that it's of use to others.
I'm using FileSystemWatcher.
It's easy when combining events with same type, here (FileSystemEventArgs), and i saw about passing extra parameters to an event Handler, but passing an event to an Event Method, i don't know how.
Before, i want to do something like this:
private void SystemWatch(object sender,
System.IO.RenamedEventArgs e,
System.IO.FileSystemEventArgs f)
But we can't change the FileSystemEventHandler or RenamedEventHandler delegate, so is there any alternative way ?
Delegates, like those found in events, define the method signature expected to be called. As such, events with differing signatures cannot implicitly call a method with a differing signature.
One potential workaround might be to use a dynamic lambda method to perform the translation:
watcher.Created += (s, a) => SystemWatch(s, null, a);
watcher.Renamed += (s, a) => SystemWatch(s, a, null);
EDIT:
After further consideration, as McGarnagle stated, RenamedEventsArgs inherits from FileSystemEventArgs. You should be able to handle both scenarios using a single event handler:
private static void OnCreatedOrRenamed(object source, FileSystemEventArgs e)
You can tell which operation it is by checking the type of e:
if (e is RenamedEventArgs)
...
else
...
You can always check if file was created or renamed with basic Changed event handler:
watcher.Changed += Watcher_Changed;
It has argument of FileSystemEventArgs type, which provides information of change type:
void Watcher_Changed(object sender, FileSystemEventArgs e)
{
switch (e.ChangeType)
{
case WatcherChangeTypes.Created:
break; // file was created
case WatcherChangeTypes.Renamed:
break; // file was renamed
}
}
I recently learned about using C# extension methods to make calling events easier and I've been using them more and more. I recently hit a strange issue that I don't understand though, and I was wondering if someone could explain it.
The issue occurs when trying to set an eventhandler extension method as an event handler of another event. Here is an example of what I'm doing:
public static class EventHandlerExtensions
{
public static void Raise<TEventArgs>(
this EventHandler<TEventArgs> eventHandler,
object sender, TEventArgs args) where TEventArgs:EventArgs
{
if (eventHandler != null)
{
eventHandler(sender, args);
}
}
}
public class Test
{
private event EventHandler<EventArgs> EventA;
private event EventHandler<EventArgs> EventB;
public Test()
{
Console.WriteLine("::Start");
EventB += EventA.Raise;
EventA += (s, a) => Console.WriteLine("Event A raised");
EventB.Raise(this, EventArgs.Empty);
Console.WriteLine("::End");
}
}
In this example, EventA should be triggered as a result of EventB being triggered. However, when I run this code, EventB fires, but the extension method on A doesn't find any listeners for it.
If I change the order around, everything works fine:
Console.WriteLine("::Start");
EventA += (s, a) => Console.WriteLine("Event A raised");
EventB += EventA.Raise;
EventB.Raise(this, EventArgs.Empty);
Console.WriteLine("::End");
Also, calling EventA.Raise from a lambda works fine:
Console.WriteLine("::Start");
EventB += (s, a) => EventA.Raise(s, a);
EventA += (s, a) => Console.WriteLine("Event A raised");
EventB.Raise(this, EventArgs.Empty);
Console.WriteLine("::End");
This is just a simple example, but I'm trying to create a class which can re-dispatch events of event sources added to it in the cleanest way possible. I don't want to create named methods just for redispatching the same events, and I'd rather not store lists of lambda functions that I can unhook from the event handlers later. Mostly, I'm just curious as to why this is happening?
Any ideas?
You capture old value of EventA into the closure by your Raise function. Since later you use += it changes value of EventA, but your closure still have an old value.
You code:
EventB += EventA.Raise;
EventA += (s, a) => Console.WriteLine("Event A raised");
Can be expanded into equivalent code which makes it clear why you get old delegate:
var oldEventA = EventA;
EventB += oldEventA.Raise; // captures old value here
// now EventA changed to new value
EventA = oldEventA + ((s, a) => Console.WriteLine("Event A raised");)
You can add following to before EventB += EventA.Raise to verify that code actually raises old event for A:
EventA += (s, a) => Console.WriteLine("Old Event A raised");
Delegate objects are immutable. Much like strings. So when you assign EventA, you create a new object. EventB is still targeting the old one, the one that didn't have any event handler assigned yet. You have to swap the two statements to fix the problem.
Sorry to ask all, but I'm an old hand Vb.net guy who's transferring to c#. I have the following piece of code that seems to activate when the (in this case) postAsync method is fired. I just don;t understand what the code is doing (as follows):-
app.PostCompleted +=
(o, args) =>
{
if (args.Error == null)
{
MessageBox.Show("Picture posted to wall successfully.");
}
else
{
MessageBox.Show(args.Error.Message);
}
};
if anyone could explain what the += (o,args) => is actually acheiving I'd be so greatful....
many thanks in advance.
Tim
(o,args) => defines a lambda expression that takes two parameters named o and args. The types of those parameters is inferred according to the type of PostCompleted (if PostCompleted is an EventHandler, then they will be respectively of type Object and EventArgs). The expression's body then follows after the =>.
The result is than added as an handler to PostCompleted.
As such, it's a less verbose way to write:
app.PostCompleted += delegate(object o, EventArgs args)
{
// ...
};
Which is a shorthand for:
void YourHandler(object o, EventArgs args)
{
// ...
}
// ...
app.PostCompleted += YourHandler;
That is an added handler for the PostCompleted event using a lambda expression. It is similar to
app.PostCompleted += MyHandler;
// ...
private void MyHandler(object sender, EventArgs e) {
// ...
}
But when using lambda expressions, you can't detach the handler easily.
It's shorthand for a delegate defining the event handler for the POST completion event:
app.PostCompleted += delegate(object o, EventArgs args) {
// ...
};
See also Anonymous Methods.
Assuming PostCompleted is an event, you are basically creating an event handler using lambda notation.
This code snipped is equivalent to:
app.PostCompleted += delegate (o, args)
{
if (args.Error == null)
{
MessageBox.Show("Picture posted to wall successfully.");
}
else
{
MessageBox.Show(args.Error.Message);
}
};
The vb.net equivalent would look like this:
AddHandler app.PostCompleted, Function(o, args)
''# ...
End Function
Note that this requires Visual Studio 2010/.Net 4, but the C# code works back in Visual Studio 2008/.Net 3.5.
But that's only partly true. In C#, this is a way to define a method as an event handler in one place. In VB.Net, you can use the Handles keyword, and so the actual equivalent might look more like this:
Public Sub App_PostCompleted(ByVal Sender as Object, ByVall e As EventArgs) Handles app.PostCompleted
''#
End Sub
But even that's not completely equivalent, since you gave the method a name and can call it from anywhere. The only reference to the C# code (and thus the only way to call it) is through the event subscription.
The (o,args) => part is a lambda expression, which is an anonymous function.
the += part assigns the lambda expression to be called when the event fires.
I have the following code where SprintServiceClient is a reference to a WCF Service-
public class OnlineService
{
private SprintServiceClient _client;
public OnlineService()
{
_client = new SprintServiceClient();
}
public void AddMemberToTeam(MemberModel user, int projectId, Action<int> callback)
{
_client.AddMemberToTeamCompleted += (s, e) => callback(e.Result);
_client.AddMemberToTeamAsync(user.ToUser(), projectId);
}
}
the problem is that every time AddMemberToTeam is called it adds another callback to client.AddMemberToTeamCompleted
i.e the first time AddMemberToTeam is called the callback is called once, the second time AddMemberToTeam is called the callback is called twice ect.
Is there any way to remove the eventhandler from AddMemberToTeamCompleted once the eventhandler has been called or use another method which takes in the callback?
You can refer to your anonymous method from inside itself as long as you assign a delegate to a variable first:
EventHandler<SomeEventArgs> handler = null;
handler = (s, e) =>
{
_client.AddMemberToTeamCompleted -= handler;
callback(e.Result);
};
_client.AddMemberToTeamCompleted += handler;
Note that you need to declare the variable and assign it separately or the compiler will deem it uninitialized when you come to use it inside the method body.
The trick to making a self-unsubscribing event-handler is to capture the handler itself so you can use it in a -=. There is a problem of declaration and definite assignment, though; so we can't do something like:
EventHandler handler = (s, e) => {
callback(e.Result);
_client.AddMemberToTeamCompleted -= handler; // <===== not yet defined
};
So instead we initialize to null first, so the declaration is before the usage, and it has a known value (null) before first used:
EventHandler handler = null;
handler = (s, e) => {
callback(e.Result);
_client.AddMemberToTeamCompleted -= handler;
};
_client.AddMemberToTeamCompleted += handler;
No there is no way,
Apparantly Tim and Marc have another nice solution
But you can always just name them, and do the -= on the named eventhandler on this method ;)
Guessing your event:
_client.AddMemberToTeamCompleted += OnAddMemberToTeamCompleted;
and
public void OnAddMemberToTeamCompleted(object sender, EventArgs args)
{
_client.AddMemberToTeamCompleted -= OnAddMemberToTeamCompleted;
callback(e.Result)
}
Next problem is getting this callback in your listener. Perhaps putting it on a Property in the EventArgs (but that feels kinda dirty, I agree)