Why does Type.IsGenericType return TRUE for Task without return type obtained by reflection from method but typeof(Task).IsGenericTyp returns FALSE

Viewed 195

Can someone explain this? Per documentation IsGenericType

indicatesing whether the current Type represents a type parameter in the definition of a generic type or method.

So this (LINQPad) code:

bool bStraight = typeof(Task).IsGenericType;
bStraight.Dump("typeof(Task).IsGenericType");

works as expected and yields the output:

typeof(Task).IsGenericType
False

But when I retrieve it from a method by reflection:

public class MyClass
{
    public async Task Method()
    {
        await Task.Run(() =>
        {
            Thread.Sleep(3000);
        });
    }
}

public async Task TEST()
{
    MyClass theObject = new MyClass();

    Task task = (Task)typeof(MyClass).GetTypeInfo()
                            .GetDeclaredMethod("Method")
                            .Invoke(theObject, null);

    bool b = task.GetType().IsGenericType;  
    bool b2 = task.GetType().GetGenericTypeDefinition() == typeof(Task<>);
    b.Dump("IsGenericType");
    b2.Dump("GetGenericTypeDefinition");

    bool bStraight = typeof(Task).IsGenericType;
    bStraight.Dump("typeof(Task).IsGenericType");
}

I get the unexpected output of :

IsGenericType
True

GetGenericTypeDefinition
True

1 Answers

In some situations, the framework returns a Task<VoidTaskResult> disguised as a Task. Imagine you have some logic relying on TaskCompletionSource<T>. If you don't actually intend to return a result, you still have to fill the T generic parameter. You could use TaskCompletionSource<object>, but you would be wasting a pointer worth of memory (4 bytes in 32 bit, 8 bytes in 64 bit). To avoid that, the framework uses an empty struct: VoidTaskResult.

Related