如何从C#类型名称字符串中获取Type?

时间:2013-12-11 17:32:54

标签: c# .net reflection formatting typename

我一直在查看Type.NamespaceType.NameType.FullNameType.AssemblyQualifiedName的返回值。存在不一致之处。

对于像ConsoleApplication8.Program+InnerClass这样的内部类,Namespace返回ConsoleApplication8Name返回InnerClass,省略Program,因此连接Type.NameSpace和{ {1}}将是类名的不完整表示(仅作为示例)。

即使Type.Name属性也不一致。虽然它省略了程序集名称并为这样的内部类返回FullName,但ConsoleApplication8.Program+InnerClassFullName等类型的泛型参数中包含程序集名称(即使外部泛型省略了它键入自己,所以我猜有一定程度的一致性。)

我目前正在使用带有缓存类型名称查找的代码,该查找使用CodeDom生成真正的C#代码名称。基本上,我试图通过给出一个真正的类名来反转进程来获取类型。

List<long>

上述函数生成友好的C#名称,如static System.Collections.Concurrent.ConcurrentDictionary<Type, string> typeNameCache = new System.Collections.Concurrent.ConcurrentDictionary<Type, string>(); static string GetTypeName(Type type) { string name; if (!typeNameCache.TryGetValue( type, out name )) { var codeDomProvider = CodeDomProvider.CreateProvider("C#"); var typeReferenceExpression = new CodeTypeReferenceExpression(new CodeTypeReference(type)); using (var writer = new StringWriter()) { codeDomProvider.GenerateCodeFromExpression(typeReferenceExpression, writer, new CodeGeneratorOptions()); name = writer.GetStringBuilder().ToString(); } typeNameCache.TryAdd( type, name ); } return name; } 。但它也产生System.Collections.Generic.List<long>之类的名称(即它使用点而不是ConsoleApplication8.Program.InnerClassProgram之间的加号)。问题是调用InnerClass将不起作用,因为它需要加号,并且有时需要汇编名称。

那么如何给出一个Type.GetType(name)对象的引用,给定一个友好的C#类名,因为它会在代码中被引用?

2 个答案:

答案 0 :(得分:4)

我已经成功实现了这一目标,每个方向都有一行代码转换为友好类型名称和运行时类型实例。难以置信。有些人说根本不可能,哈哈。无瑕疵。

static Type GetType( string friendlyName )
{
    return (Type)(new CSharpCodeProvider().CompileAssemblyFromSource( new CompilerParameters( AppDomain.CurrentDomain.GetAssemblies().SelectMany<Assembly,string>( a => a.GetModules().Select<Module,string>( 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));
}

以上代码(仅限第一种方法),当扩展为多行时,如下所示。你可以像GetType("System.Collections.Generic.List<int>");那样进行调用,它会返回一个类型引用。

static Type GetType( string friendlyName )
{
    var currentlyLoadedModuleNames = AppDomain.CurrentDomain.GetAssemblies().SelectMany<Assembly,string>( a => a.GetModules().Select<Module,string>( 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 );
}

更新:我添加了一行,使当前app域中加载的所有模块都可供编译器使用。这应该保证这能够通过名称获取任何类型,就好像你已经在代码中直接引用它一样,除了所需的类型必须是公共的,因为它们实际上是从编译器中的外部程序集引用的。而不是直接在当​​前正在执行的程序集中。

就像测试一样,返回的类型应该在缓存中工作,因为:

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

答案 1 :(得分:1)

因此,基本上根据我们在评论中的对话,这里的目标是从类型的名称获取类型对象(出于任何目的)。调用“Type.GetType()”适用于简单类型,但对于泛型和其他类型,它不起作用,因为名称需要限定。关键,那就是使用代码编译器实际上有C#代码引擎查找并获取类型。以下是一个工作程序:

using System;
using System.Reflection;
using Microsoft.CSharp;
using System.CodeDom.Compiler;

namespace SimpleCompileTest
{
    class Program
    {
        public static void Main(string[] args)
        {
            string typeName = "System.Collections.Generic.List<int>";
            Type theType = GetTypeFromName(typeName);
        }

        private static Type GetTypeFromName(string typeName)
        {
            // double open and close are for escape purposes
            const string typeProgram = @"using System; using System.Collections.Generic; using System.IO;
                namespace SimpleTest
                {{
                    public class Program
                    {{
                        public static Type GetItemType()
                        {{
                            {0} typeTest = new {0}();
                            if (typeTest == null) return null;
                            return typeTest.GetType();
                        }}
                    }}
                }}";

            var formattedCode = String.Format(typeProgram, typeName);

            var CompilerParams = new CompilerParameters
                {
                    GenerateInMemory = true,
                    TreatWarningsAsErrors = false,
                    GenerateExecutable = false,
                    CompilerOptions = "/optimize"
                };

            string[] references = { "System.dll" };
            CompilerParams.ReferencedAssemblies.AddRange(references);

            var provider = new CSharpCodeProvider();
            CompilerResults compile = provider.CompileAssemblyFromSource(CompilerParams, formattedCode);
            if (compile.Errors.HasErrors) return null;


            Module module = compile.CompiledAssembly.GetModules()[0];
            Type mt = null; MethodInfo methInfo = null;

            if (module != null) mt = module.GetType("SimpleTest.Program");
            if (mt != null) methInfo = mt.GetMethod("GetItemType");
            if (methInfo != null) return (Type)methInfo.Invoke(null, null);

            return null;
        }
    }
}

需要注意的一件非常重要的事情 - 您需要将编译器列表添加到您希望从中提取类型的编译器中。这意味着如果你有一个你想要引用的自定义类型,你需要将它提供给编译器,否则,这是有效的!享受!