Search code examples
c#.netreflectiontypedescriptor

TypeDescriptor.GetConverter() doesnt return my converter


I have a simple type with a custom type converter that is compiled and loaded at runtime. TypeDescriptor.GetConverter() doesn't find the right converter though. Here is a stand-alone example

using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.CodeDom.Compiler;
using Microsoft.CSharp;

public class Program
{
    private static string src =
@"
using System;
using System.ComponentModel;
namespace LoadMe
{
    [TypeConverter(typeof(FooConverter))]
    public class Foo
    {
    }
    public class FooConverter : TypeConverter
    {
        // stuff
    }
}
";
    public static void Main()
    {
        var codeProvider        = new CSharpCodeProvider(new     Dictionary<string, string>{{ "CompilerVersion", "v4.0" }});
        var compileParameters   = new CompilerParameters(new[] { "System.dll"     }) { GenerateInMemory = true };
        var compilerResults     = codeProvider.CompileAssemblyFromSource(compileParameters, src);

        if (compilerResults.Errors.Count == 0)
        {
            var fooType = compilerResults.CompiledAssembly.GetType("LoadMe.Foo");
            Console.WriteLine(fooType.FullName + "::" + fooType.Assembly.FullName);
            Console.WriteLine("Type converter type = '" + TypeDescriptor.GetConverter(fooType).GetType().FullName + "'");
        }
        else 
        {
            foreach (var err in compilerResults.Errors)
                Console.WriteLine(err);
        }
    }
}

Here is the output of the example (compiled in vs)

LoadMe.Foo::q5sszdls, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
Type converter type = 'System.ComponentModel.TypeConverter'

Should I just dig out the attribute and create the instance myself, or am I missing something else here?

ALSO odd! When ran in LINQPad as a 'C# program', it actually works! here is the output. LINQPad ready source

LoadMe.Foo::oqmid5in, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
Type converter type = 'LoadMe.FooConverter'

Any idea what LINQPad actually does differently to have it find the right converter?


Solution

  • .NET uses reflection to load the specified type converter. When you pass a Type instance to TypeConverterAttribute, your TypeConverterAttribute will contain an assembly-qualified type name. Loading that assembly-qualified type name does not work, because the assembly cannot be found. (Yes, even though that assembly is already loaded.)

    You can use the constructor overload that takes a string to specify the type name without any references to the defining assembly, in which case it will be looked up in the same assembly as the type whose converter you're requesting:

    [TypeConverter("LoadMe.FooConverter")]
    public class Foo
    {
    }