How to get a Type from a C# type name string?

后端 未结 2 731
谎友^
谎友^ 2021-01-25 05:27

I\'ve been looking at return values for Type.Namespace, Type.Name, Type.FullName, and Type.AssemblyQualifiedName. There are

2条回答
  •  不思量自难忘°
    2021-01-25 06:23

    I've managed to achieve this now with a single line of code in each direction translating to and from friendly type names and runtime Type instances. Incredible. And some said it was not possible at all, lol. Flawless.

    static Type GetType( string friendlyName )
    {
        return (Type)(new CSharpCodeProvider().CompileAssemblyFromSource( new CompilerParameters( AppDomain.CurrentDomain.GetAssemblies().SelectMany( a => a.GetModules().Select( m => m.FullyQualifiedName )).ToArray(), null, false) {GenerateExecutable = false, GenerateInMemory = true, TreatWarningsAsErrors = false, CompilerOptions = "/optimize"}, "public static class C{public static System.Type M(){return typeof(" + friendlyName + ");}}").CompiledAssembly.GetExportedTypes()[0].GetMethod("M").Invoke( null, System.Reflection.BindingFlags.Static, null, null, null ));
    }
    
    static string GetFriendlyName( Type type )
    {
        return new CSharpCodeProvider().GetTypeOutput(new CodeTypeReference(type));
    }
    

    The above code (first method only), when expanded to multiple lines looks like the following. You can just make a call like GetType("System.Collections.Generic.List"); and it will return a type reference.

    static Type GetType( string friendlyName )
    {
        var currentlyLoadedModuleNames = AppDomain.CurrentDomain.GetAssemblies().SelectMany( a => a.GetModules().Select( m => m.FullyQualifiedName )).ToArray();
        var csc = new CSharpCodeProvider();
        CompilerResults results = csc.CompileAssemblyFromSource(
            new CompilerParameters( currentlyLoadedModuleNames, "temp.dll", false) {
                GenerateExecutable = false, GenerateInMemory = true, TreatWarningsAsErrors = false, CompilerOptions = "/optimize"
            },
            @"public static class TypeInfo {
                public static System.Type GetEmbeddedType() {
                return typeof(" + friendlyName + @");
                }
            }");
        if (results.Errors.Count > 0)
            throw new Exception( "Error compiling type name." );
        Type[] type = results.CompiledAssembly.GetExportedTypes();
        return (Type)type[0].GetMethod("GetEmbeddedType").Invoke( null, System.Reflection.BindingFlags.Static, null, null, null );
    }
    

    Update: I added a line to make all the modules loaded in the current app domain available to the compiler. That should guarantee that this is able to acquire any type by name as if you had referenced it directly in your code, with the exception that the desired types have to be public, since they are essentially referenced from an external assembly from within the compiler rather than directly in the currently executing assembly.

    Just as as test, the returned type should work in a cache, since:

    Type t = GetType( "System.Collections.Generic.List" );
    Console.WriteLine( typeof(System.Collections.Generic.List) == t );
    //RETURNS TRUE
    

提交回复
热议问题