繁体   English   中英

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

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

我一直在寻找Type.NamespaceType.NameType.FullNameType.AssemblyQualifiedName返回值。 有不一致之处。

对于类似ConsoleApplication8.Program+InnerClass的内部类,名称空间返回ConsoleApplication8Name返回InnerClass ,省略Program ,因此将Type.NameSpaceType.Name串联Type.Name将是类名的不完整表示(仅作为示例)。

甚至FullName属性也不一致。 尽管它省略了程序集名称并为此类内部类返回ConsoleApplication8.Program+InnerClass ,但是FullName在诸如List<long>的类型的通用参数中包括了程序集名称(即使外部通用类型本身被省略了,所以我猜测那里有一定程度的一致性)。

我目前正在将此代码与使用CodeDom生成真实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;
}

上面的函数产生友好的C#名称,例如System.Collections.Generic.List<long> 但是它还会产生类似ConsoleApplication8.Program.InnerClass名称(即,它在ProgramInnerClass之间使用点而不是加号)。 问题在于调用Type.GetType(name)将不起作用,因为它将需要加号,并且有时还需要程序集名称。

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

现在,我已经成功地通过在每个方向上与友好类型名称和运行时Type实例之间进行转换的一行代码来实现这一目标。 难以置信。 有人说根本不可能,哈哈。 无瑕。

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>");那样进行调用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 );
}

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

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

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

因此,基本上,根据我们在注释中的对话,此处的目标是从类型名称中获取类型对象(出于任何目的)。 调用“ 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;
        }
    }
}

需要注意的一件非常重要的事情-您需要将汇编程序列表添加到希望从中提取类型的编译器。 这意味着,如果您有要引用的自定义类型,则需要将其提供给编译器,否则,它将起作用! 请享用!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM