Edit: Moved the actual question to the top.
Update: Found an example by Microsoft, tucked on some more code at the end.
My questions are these:
- Is it safe to call multiple BeginInvoke calls on the same delegate instance, or do I have to construct a new delegate instance for each in-flight method call?
- If I have to construct new instances for each, is there some way to get hold of the original delegate out of the IAsyncResult value?
- Is there some other, better, way to add asynchronous support to my class than using delegates?
More info follows.
I am adding asynchronous support to a class of mine, and thought I'd do it simple.
Take this class:
public class Calculator
{
public Int32 Add(Int32 a, Int32 b)
{
return a + b;
}
}
I thought I could simply do this:
public class Calculator
{
private delegate Int32 AddDelegate(Int32 a, Int32 b);
public Int32 Add(Int32 a, Int32 b)
{
return a + b;
}
public IAsyncResult BeginAdd(Int32 a, Int32 b,
AsyncCallback callback, Object obj)
{
return new AddDelegate(Add).BeginInvoke(a, b, callback, obj);
}
public Int32 EndAdd(IAsyncResult ar)
{
return new AddDelegate(Add).EndInvoke(ar);
}
}
This doesn't work, as the two methods each construct their own delegate object, and the .EndInvoke call checks to see if the delegate instance i call it on is the same as the one I originally called BeginInvoke on.
The simplest way to handle this would be to just store a reference into a variable, like this:
public class Calculator
{
private delegate Int32 AddDelegate(Int32 a, Int32 b);
private AddDelegate _Add;
public Calculator()
{
_Add = new AddDelegate(Add);
}
public Int32 Add(Int32 a, Int32 b)
{
return a + b;
}
public IAsyncResult BeginAdd(Int32 a, Int32 b,
AsyncCallback callback, Object obj)
{
return _Add.BeginInvoke(a, b, callback, obj);
}
public Int32 EndAdd(IAsyncResult ar)
{
return _Add.EndInvoke(ar);
}
}
Note that I'm fully aware of problems with allowing multiple instance methods on the same class to execute at the same time, with regards to shared state, etc.
Update: I found this example here by Microsoft on Asynchronous Delegates Programming Sample. It shows casting the IAsyncResult reference back to an AsyncResult object, and then I can get the original delegate instance through the AsyncDelegate property.
Is this a safe approach?
In other words, is the following class fine?
public class Calculator
{
private delegate Int32 AddDelegate(Int32 a, Int32 b);
public Int32 Add(Int32 a, Int32 b)
{
return a + b;
}
public IAsyncResult BeginAdd(Int32 a, Int32 b, AsyncCallback callback, Object obj)
{
return new AddDelegate(Add).BeginInvoke(a, b, callback, obj);
}
public Int32 EndAdd(IAsyncResult ar)
{
AddDelegate del = (AddDelegate)((AsyncResult)ar).AsyncDelegate;
return del.EndInvoke(ar);
}
}
I always store the delegate instance I called it on as part of the AsyncState of a BeginInvoke, that way you can always get it without having to rely on a specific implementation of IAsyncResult
Once you've got your IAsyncResult:
Edit: if you just mean the delegate itself - I think you can just do:
You could always capture it into the delegate; something like here: Async without the Pain, which lets you just use an
Action
callback (orAction<T>
).Other common patterns involve events for the callback, and perhaps
ThreadPool.QueueUserWorkItem
; a lot simpler thanIAsyncResult
.Putting all this together; here's an example where neither the caller nor the code needs to get stressed about
IAsyncResult
: