Why can't GetType() find types when invoked through a method group delegate?

We have a very simple program invoking the Type.GetType static method. Both examples should return a valid type instance. Only the second one actually is. Looks like something odd is happening with the stack crawl used by GetType, but what exactly is the issue here? Is it bug or some obscure feature?

public class TestClass { }

class Program
{
    static void Main(string[] args)
    {
        var fullName = typeof(TestClass).FullName;
        Console.WriteLine("Full name: {0}", fullName);

        new[] { fullName }.Select(Type.GetType).ToList().ForEach(t => Console.WriteLine("Method group: '{0}'", t));
        new[] { fullName }.Select(t => Type.GetType(t)).ToList().ForEach(t => Console.WriteLine("Closure: '{0}'", t));
    }
}

Running:

Full name: GetTypeBeingWeird.TestClass
Method group: ''
Closure: 'GetTypeBeingWeird.TestClass'
Jon Skeet
people
quotationmark

This is really interesting. It's a mixture of the behaviour of Type.GetType(string) in terms of the calling assembly, and how method group conversions work.

First, the Type.GetType documentation includes this:

If typeName includes the namespace but not the assembly name, this method searches only the calling object's assembly and Mscorlib.dll, in that order.

In your first call, you're passing in a delegate which calls Type.GetType... but it isn't particularly called from your assembly. It's effectively called directly from the Select method in LINQ... if you looked at the stack trace from within Type.GetType, you'd see Select as the direct caller, I believe.

In your second call, you're passing in a closure which calls Type.GetType, and that call is within your assembly.

That's why it finds the type in the second case but not the first. This is further validated by specifying a type which is in the LINQ assembly:

var fullName = typeof(Enumerable).FullName;

Then the results are the opposite way round:

Full name: System.Linq.Enumerable
Method group: 'System.Linq.Enumerable'
Closure: ''

If you specify something in mscorlib (e.g. typeof(string).FullName) then both approaches work:

Full name: System.String
Method group: 'System.String'
Closure: 'System.String'

The way to get around this oddity when looking for your class, still using a method group, is simply to supply the assembly-qualified name instead:

var fullName = typeof(TestClass).AssemblyQualifiedName;

people

See more on this question at Stackoverflow