Search code examples
c#types.net-assemblytypebuilder

Retrieve Type Foo<Bar> from name when Bar is created from TypeBuilder


I am creating types using TypeBuilder. These types are then used as a arguments in a generic class. What I want to do is create the Type from its name. The fact that the generic parameter is dynamic seems to be a barrier.

static public class DynamicTypeTest
{
    public class Generic<T> { }

    static public void Test()
    {
        Type dynamicType = createDynamicType();
        Type genericType = typeof(Generic<>);

        // Generic<DynamicType>
        Type genericDynamicType = genericType.MakeGenericType(new Type[]{dynamicType});

        Debug.Assert(TypeFromName(genericDynamicType.FullName) == genericDynamicType);              // fail
        Debug.Assert(TypeFromName(genericDynamicType.AssemblyQualifiedName) == genericDynamicType); // fail
        Debug.Assert(TypeFromName(genericDynamicType.Name) == genericDynamicType);                  // fail

        // Generic<int>
        Type genericIntType = genericType.MakeGenericType(new Type[] { typeof(int) });

        Debug.Assert(TypeFromName(genericIntType.FullName) == genericIntType); // This succeeds. Replacing 'int' with a Type defined in another project also works
    }

    // This is the essence of what I want to do
    static private Type TypeFromName(string name)
    {
        return Assembly.GetExecutingAssembly().GetType(name);
    }

    static private Type createDynamicType()
    {
        AssemblyName assemblyName = new AssemblyName("DynamicAssembly");
        AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
        ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicModule");
        TypeAttributes typeAttributes = TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass;
        TypeBuilder typeBuilder = moduleBuilder.DefineType("DynamicType", typeAttributes, typeof(System.Object));

        return typeBuilder.CreateType();
    }
}

Obviously I know how to create the Type from MakeGenericType, but in the real-life scenario I am working with, I don't want to (or can't) mangle the type name so that I can create the types separately and piece them together.

How can I recreate this kind of Type from a string name?


Solution

  • It occurred to me that I should use the version of Assembly.GetType() that throws an Exception on error. It threw a FileNotFoundException, so apparently, in the generic arguments, only assemblies from file are allowed. To get around this, I registered for the AssemblyResolve event from AppDomain, and handled the case that DynamicAssembly was not found on file. My working code:

    static public class DynamicTypeTest
    {
        public class Generic<T> { }
    
        static public void Test()
        {
            AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
    
            Type dynamicType = createDynamicType();
            Type genericType = typeof(Generic<>);
    
            // Generic<DynamicType>
            Type genericDynamicType = genericType.MakeGenericType(new Type[]{dynamicType});
    
            Debug.Assert(TypeFromName(genericDynamicType.FullName) == genericDynamicType); // pass
    
            AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve;
        }
    
        static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                AssemblyName assemblyName = assembly.GetName();
    
                if (args.Name == assemblyName.FullName)
                {
                    return assembly;
                }
            }
    
            return null;
        }
    
        // This is the essence of what I want to do
        static private Type TypeFromName(string name)
        {
            try
            {
                return typeof(Generic<>).Assembly.GetType(name, true);
            }
            catch (Exception)
            {
                return null;
            }
        }
    
        static private Type createDynamicType()
        {
            AssemblyName assemblyName = new AssemblyName("DynamicAssembly");
            AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicModule");
            TypeAttributes typeAttributes = TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass;
            TypeBuilder typeBuilder = moduleBuilder.DefineType("DynamicType", typeAttributes, typeof(System.Object));
    
            return typeBuilder.CreateType();
        }