7

I am working on a C# system and a class has one function that returns a System.Threading.Tasks.Task object and has a property System.Type ReturnType.

When ReturnType is null i know the method return a Task object. But sadly there is no way to know if the class implementing the interface will return a Task<ReturnType> or Task<object> and i need to get the result of this method. I think the easiest way to do it would be to convert the Task<T> to Task<object> so i can get the result and handle it using the Type value in ReturnType.

How can i convert a Task<T> to Task<object> without knowing the type of T ?

public interface ITaskFactory
{

    ReadOnlyCollection<ParameterInfo> ParametersInfo { get; }

    Type ReturnType { get; }

    Task CreateTask (params object[] args);

}

I need to get the result returned by the Task that i received by calling CreateTask()

See: http://dotnetfiddle.net/Bqwz0I

madrang
  • 804
  • 8
  • 14

3 Answers3

6

I have tried quite a few things and the only things that has worked for me is using reflection. I also added code that detects tasks without a value and throws an exception.

using System;
using System.Threading.Tasks;
using System.Reflection;

public class Program
{
    private static async Task<object> Convert(Task task)
    {
        await task;
        var voidTaskType = typeof (Task<>).MakeGenericType(Type.GetType("System.Threading.Tasks.VoidTaskResult"));
        if (voidTaskType.IsAssignableFrom(task.GetType()))
            throw new InvalidOperationException("Task does not have a return value (" + task.GetType().ToString() + ")");
        var property = task.GetType().GetProperty("Result", BindingFlags.Public | BindingFlags.Instance);
        if (property == null)
            throw new InvalidOperationException("Task does not have a return value (" + task.GetType().ToString() + ")");
        return property.GetValue(task);
    }

    public static async Task Main()
    {
        Console.WriteLine("Start");
        Task i = CreateTask();
        Task<object> o = Convert(i);
        Console.WriteLine("value: {0}", await o);
        Console.WriteLine("value2: {0}", await Convert(Task.FromResult<int>(123)));

        //Test for tasks without return values
        try
        {
            Console.WriteLine("value3: {0}", await Convert(Task.CompletedTask));
        }
        catch (Exception ex)
        {
            Console.WriteLine("value3: EX {0}", ex.Message);
        }

        //Test for tasks without return values
        try
        {
            Console.WriteLine("value4: {0}", await Convert(Test4()));
        }
        catch (Exception ex)
        {
            Console.WriteLine("value4: EX {0}", ex.Message);
        }
        Console.WriteLine("Done");
    }


    private static Task CreateTask()
    {
        return Task.FromResult("Some result");
    }

    public static async Task Test4()
    {
        await Task.CompletedTask;
        return;
    }
}
webwake
  • 1,154
  • 1
  • 13
  • 26
-2

Task<T> is not covariant (unlike IEnumerable<T>, see its definition, notice the out keyword) so you can only await it first then convert:

async Task<object> Do<T>(Task<T> task)
{
   // this won't compile: Cannot implicitly convert type 'Task<T>' to 'Task<object>'
   // return task;

    object result = await task;
    return result;
}
abatishchev
  • 98,240
  • 88
  • 296
  • 433
  • See this [fiddle](http://dotnetfiddle.net/sdV9LB) – abatishchev Mar 01 '14 at 02:30
  • 1
    I tried the code provided and when i compile i get CommandLineInterface.cs(24,24): Error CS0411: The type arguments for method `Linsft.Software.CommandLineInterface.Convert(System.Threading.Tasks.Task)' cannot be inferred from the usage. Try specifying the type arguments explicitly (CS0411) (Software) It want me to specify the type manualy, but i can't since i don't know what type it will be.... – madrang Mar 01 '14 at 02:41
  • 1
    Same error here http://dotnetfiddle.net/Bqwz0I – madrang Mar 01 '14 at 02:43
  • @madrang: That's another question of conversion between `Task` and `Task`. – abatishchev Mar 01 '14 at 02:45
  • @madrang: this works http://dotnetfiddle.net/3gtxTJ but is different from you're looking for I guess. – abatishchev Mar 01 '14 at 02:46
  • 1
    Yea, because in the example you know that the type is Task, thats why it works. In my case it can be anything... – madrang Mar 01 '14 at 02:48
  • 2
    `Task` can't be covariant, only interfaces and delegates can. – svick Mar 01 '14 at 12:21
  • It's not another question -- it's literally THIS question. Read it. – BrainSlugs83 May 05 '17 at 16:59
-2

Easiest way is to use ContinueWith

    Task<T> task = null;
    Task<object> obj = task.ContinueWith(t => (object)t.Result);
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454