Too many arguments in BeginXXX for FromAsync?
Asked Answered
M

3

15

I have an async method with the following signature:

IAsyncResult BeginGetMyNumber(string foo, string bar, string bat, int bam, AsyncCallback callback, object state)

I want to execute it using Factory.FromAsync like this:

var result  = Task<int>.Factory.FromAsync(
                instance.BeginGetMyNumber, 
                instance.EndGetMyNumber, 
                "foo",
                "bar",
                "bat",
                100, /*bam*/
                null);

but I get the following error:

Argument 1: cannot convert from 'method group' to 'System.Func'

It seems there is no suitable overloaded FromAsync method http://msdn.microsoft.com/en-us/library/system.threading.tasks.taskfactory.fromasync.aspx, it only supports up to 5 arguments (including callback and state) on the BeginXXX method.

Other than refactoring the BeginXXX method to take an object rather than six arguments, is there a way to execute it using FromAsync?

Mortensen answered 23/3, 2013 at 1:42 Comment(0)
M
7

Actually it seems I can use the overloaded method for Factory.FromAsync( that takes an IAsyncResult object as the first argument and a callback method as the second:

result = Task<string>.Factory.FromAsync(
                instance.BeginGetMyNumber("foo", "bar", "bat", 1, null, null),
                instance.EndGetMyNumber);
Mortensen answered 24/3, 2013 at 22:48 Comment(3)
With this overload, FromAsync doesn't get a chance to pass a callback to your BeginGetMyNumber method - you've already passed null. Instead, this overload will use IAsyncResult.WaitHandle to be notified when the operation completes. This is less efficient - see blogs.msdn.com/b/pfxteam/archive/2012/02/06/10264610.aspx for details.Blackguard
It works for four parameters, but I have five parameters same not works for my case. What should I do ?Cabbagehead
Microsoft doesn't recommend using this overload see Tip in learn.microsoft.com/en-us/dotnet/api/…Distended
V
9
result = Task<string>.Factory.FromAsync(
  (callback, state) => instance.BeginGetMyNumber("foo", "bar", "bat", 1, callback, state),
   instance.EndGetMyNumber, state: null);

This technique (partial function application) works for begin methods with any number of input params.

Voiceless answered 2/7, 2014 at 15:49 Comment(2)
this overload of FromAsync requires the state as a third param. Other than that worked for me.Graduate
Well, this was posted over a year after I'd already accepted my own answer. Damn you, entropy!Mortensen
M
7

Actually it seems I can use the overloaded method for Factory.FromAsync( that takes an IAsyncResult object as the first argument and a callback method as the second:

result = Task<string>.Factory.FromAsync(
                instance.BeginGetMyNumber("foo", "bar", "bat", 1, null, null),
                instance.EndGetMyNumber);
Mortensen answered 24/3, 2013 at 22:48 Comment(3)
With this overload, FromAsync doesn't get a chance to pass a callback to your BeginGetMyNumber method - you've already passed null. Instead, this overload will use IAsyncResult.WaitHandle to be notified when the operation completes. This is less efficient - see blogs.msdn.com/b/pfxteam/archive/2012/02/06/10264610.aspx for details.Blackguard
It works for four parameters, but I have five parameters same not works for my case. What should I do ?Cabbagehead
Microsoft doesn't recommend using this overload see Tip in learn.microsoft.com/en-us/dotnet/api/…Distended
S
5

Yeah, basically, you've run out of arguments. :(

The FromAsync method only takes a maximum of three passed-to-the-async-call arguments, spelled out in full like so:

var result  = Task<int>
    .Factory
    .FromAsync<string,string,string>(
        BeginGetMyNumber,
        EndGetMyNumber,
        "foo",
        "bar",
        "bat",
        null);

Which would work if you had:

IAsyncResult BeginGetMyNumber(
   string foo, 
   string bar, 
   string bat, 
   AsyncCallback callback, 
   object state)
{
}

But ye've got one too many.

Ooh, got something that might help - you WILL want to clean this up, this is extremely thrown-together!!!

public static class Ext
{
    public static Task<TResult> FromAsync<TArg1, TArg2, TArg3, TArg4, TResult>(
        this TaskFactory<TResult> factory,
        Func<TArg1,TArg2,TArg3,TArg4,AsyncCallback, object, IAsyncResult> beginMethod, 
        Func<IAsyncResult, TResult> endMethod, 
        TArg1 arg1,
        TArg2 arg2,
        TArg3 arg3,
        TArg4 arg4,
        object state,
        TaskCreationOptions creationOptions = TaskCreationOptions.None, 
        TaskScheduler scheduler = null)
    {
        scheduler = scheduler ?? TaskScheduler.Current;
        AsyncCallback callback = null;
        if (beginMethod == null)
        {
            throw new ArgumentNullException("beginMethod");
        }
        if (endMethod == null)
        {
            throw new ArgumentNullException("endMethod");
        }
        TaskCompletionSource<TResult> tcs = 
             new TaskCompletionSource<TResult>(state, creationOptions);
        try
        {
            if (callback == null)
            {
                callback = delegate (IAsyncResult iar) 
                {
                    tcs.TrySetResult(endMethod(iar));
                };
            }
            beginMethod(arg1, arg2, arg3, arg4, callback, state);
        }
        catch
        {
            tcs.TrySetResult(default(TResult));
            throw;
        }
        return tcs.Task;
    } 
}
Synthetic answered 23/3, 2013 at 2:8 Comment(1)
That's pretty cool - but it looks like I can just use a different overloaded method.Mortensen

© 2022 - 2024 — McMap. All rights reserved.