1

I would like to cast a Task<object> to Task<?> at the runtime like that :

return task.CastTo(targetType);

Here is my best approach:

    [TestMethod]
    public void CastToString()
    {
        var task = CallTaskString("test");
        var casting = CastTo<string>(task);
        Assert.IsNotNull(casting);
        var targetTask = casting as Task<string>;
        Assert.IsNotNull(targetTask);
    }

    private async Task<T> CastTo<T>(Task task)
    {
       await task.ConfigureAwait(false);
        return (T)((dynamic)task).Result;
    }

    public Task<object> CallTaskString(string value)
    {
        return Task.Run<object>(() => value);
    }

I just want to replace the T by an argument Type targetType like that: public static async Task CastTo(this Task task, Type targetType).

Thanks

Yann
  • 41
  • 5
  • I don't think it is possible. What should the return type of the method look like? `private async Task> CastTo(this Task tas, Type targetType)`? – SomeBody Aug 06 '21 at 07:15
  • Task in the definition but in the value Task – Yann Aug 06 '21 at 07:20
  • 4
    Generics are (mostly) a compile-time tool. If you can't access the concrete type of `Task>` in code (because it's unknown at compile-time), I don't see much benefit over just using `Task` or `Task`. What is your use case for this? This smells like an XY problem. – Heinzi Aug 06 '21 at 07:22
  • I use ImpromptuInterface.ActLike to create a proxy object who works with sync methods but not with async because he can't cast Task to Task – Yann Aug 06 '21 at 07:24
  • Why do you want to cast the task and not just the result? Also, `Task` is not a `Task` so a cast isn't possible; you would need to perform a conversion. – Johnathan Barclay Aug 06 '21 at 08:07
  • Because I need to respect an interface and the methods returns a Task (or other Task, Task ...) and the proxy perform a Task and at this moment I have an exception – Yann Aug 06 '21 at 08:58

2 Answers2

1

It's possible to call a generic method at runtime. Maybe this can help you :

public static class TaskExtension
{
    public static Task CastTo(this Task task, Type targetType)
    {
        var taskType = task.GetType();
        var srcType = taskType.GetGenericArguments().First();
        var method = typeof(TaskExtension).GetMethods(BindingFlags.NonPublic | BindingFlags.Static).Single(m => m.Name == nameof(CastToImpl)).MakeGenericMethod(srcType, targetType);
        return (Task)method.Invoke(null, new[] { task });
    }

    private static Task<R> CastToImpl<T, R>(Task<T> t)
        where R : T
    {
        return t.ContinueWith(t => (R)t.Result); 
    }
}

You can use like :

[TestMethod]
public void CastToString1()
{
    var task = CallTaskString("test");
    var casting = task.CastTo(typeof(string));
    Assert.IsNotNull(casting);
    var targetTask = casting as Task<string>;
    Assert.IsNotNull(targetTask);
}
vernou
  • 6,818
  • 5
  • 30
  • 58
-1

I would like to cast a Task to Task<?> at the runtime

That isn't possible if the T in Task<T> is anything other than object, because Task<T> is invariant.

You seem to be overthinking this; there's no need to invent a generic way of converting the Task, you can simply cast the Result of the Task<object>:

public async Task<string> SomeInterfaceMethodAsync()
{
    return (string) await SomeMethodThatReturnsTaskObjectAsync();
}
Johnathan Barclay
  • 18,599
  • 1
  • 22
  • 35
  • No good solution, I found a workaround by overriding the proxy method and pass the result in the parameters instead of return. It's dirty but it works – Yann Aug 06 '21 at 09:50