CodeMonkey
CodeMonkey

Reputation: 12424

c# asynchronous invoke of generic delegates

I want to invoke a general Delegate using async await. I am not working with a prebuilt delegates, but rather the general Delegate class which can receive arguments as an array of objects.

I am doing something like this:

public object InvokeAction(Delegate action, object[] actionArgs = null)
    {
        if(action == null)
        {
            return null;
        }
        return action.DynamicInvoke(args);
    }

What I want to do is give an option to run the delegate using await but since DynamicInvoke returns an object, it doesn't have an awaiter.

Is there a way to define a general delegate and make an asynchronous invocation? If not a generic one, is there some version which is close to a generic delegate (It's ok to force the user to some delegate definition) which can be invoked the way I want?

Upvotes: 3

Views: 1422

Answers (2)

Johnathan Barclay
Johnathan Barclay

Reputation: 20354

Your delegate needs to return an awaitable type, generally this is Task or Task<TResult>.

You could check for this at runtime:

public async Task<TResult> InvokeAction<TResult>(Delegate action, object[] actionArgs = null)
{
    //...
    var result = action.DynamicInvoke(actionArgs);
    if (result is Task<TResult> task) return await task;
    return (TResult)result;
}

However, as long as actionArgs are not modified within your method, you could statically type your delegate, and use a closure:

public async Task<TResult> InvokeAction<TResult>(Func<Task<TResult>> action)
{
    //...
    return await action();
}

var result = InvokeAction(() => YourMethodAsync(arg1, arg2));

Upvotes: 3

Evk
Evk

Reputation: 101443

I assume that IF target delegate references to method which is async (returns Task or Task<T>) then you want to execute it asynchronously. Then you can use method similar to your current one, but which is async:

public static async Task<object> InvokeActionAsync(Delegate action, object[] actionArgs = null)
{
    if (action == null) {
        return null;
    }

    // invoke it
    var result = action.DynamicInvoke(actionArgs);
    if (result == null)
        return null;
    if (result.GetType().IsGenericType && result.GetType().GetGenericTypeDefinition() == typeof(Task<>)) {
        // this is some Task<T> which returns result
        await (Task) result;
        // now need to grab the result
        return result.GetType().GetProperty("Result").GetValue(result);
    }
    else if (result is Task) {
        // it's regular Task which does not return the value
        await (Task) result;
        return null;
    }
    // otherwise nothing to really run async, just return result
    return result;
}

Upvotes: 1

Related Questions