如何从一个C#类型名称字符串类型?类型、字符串、名称

2023-09-03 07:46:43 作者:有颜色de狼

我一直期待在返回值 Type.Namespace Type.Name Type.FullName Type.AssemblyQualifiedName 。有不一致的地方。

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

有关的内部类如 ConsoleApplication8.Program +将InnerClass ,命名空间返回 ConsoleApplication8 名称返回将InnerClass ,省略计划,所以串联类型。命名空间 Type.Name 将类名(就像一个例子)的不完全重新presentation。

For an inner class like ConsoleApplication8.Program+InnerClass, Namespace returns ConsoleApplication8 and Name returns InnerClass, omitting Program, so concatenating Type.NameSpace and Type.Name would be an incomplete representation of the class name (just as an example).

甚至全名属性不一致。虽然它忽略了程序集的名称,并返回 ConsoleApplication8.Program +将InnerClass 对于这样的内部类,全名包含在程序集名称一类通用参数,如名单,其中,长> (即使它省略了外泛型类型本身,所以我想有一致性的某种程度的存在)

Even the FullName property is inconsistent. Although it omits assembly name and returns ConsoleApplication8.Program+InnerClass for such an inner class, FullName includes the assembly name in the generic arguments for a type such as List<long> (even though it's omitted for the outer generic type itself, so I guess there is some level of consistency there).

我目前使用这种code与使用codeDOM产生真正的C#code名称的缓存类型的名称查找。基本上,我试图扭转过程中得到的类型,给出一个真实的类名。

I am currently using this code with a cached type name lookup that uses the CodeDom to produce real C# code names. Basically, I'm trying to reverse the process to get the type, given a real class name.

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&LT型C#的名称;长&GT; 。但它也产生的名称,如 ConsoleApplication8.Program.InnerClass (即它使用一个加号的一个点,而不是计划之间和将InnerClass )。问题是,调用 Type.GetType(名称)将无法正常工作,因为它需要的加号,在那里,另外有时需要程序集名称。

The above function produces friendly C# name like System.Collections.Generic.List<long>. But it also produces names like ConsoleApplication8.Program.InnerClass (i.e. it uses a dot instead of a plus sign between Program and InnerClass). The problem is that calling Type.GetType(name) won't work, because it would require the plus sign to be there, and in addition it sometimes requires the assembly name.

所以,我怎么能得到一个提述键入的对象,因为它会在code中引用一个友好的C#类的名字?

So how can I get a references to a Type object, given a friendly C# class name as it would be referenced in code?

推荐答案

我已经成功现在的code一行在每个方向平移,并从友好型名称和运行时类型的实例来实现这一目标。难以置信。而一些说,这是根本不可能在上海。完美无瑕的。

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<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));
}

以上code(第一种方法只),当扩展到多行看起来像下面这样。你可以只让像的GetType调用(System.Collections.Generic.List&LT; INT&GT;); 键,它会返回一个类型引用

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<int>"); and it will return a type reference.

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 );
}

更新:我添加了一行,使所有可用的编译器目前的应用程序域加载的模块。这应该保证这是能够获得任何类型的名字,如果你已经在你的code直接引用它,所不同的是所需要的类型必须是公开的,因为他们基本上是从外部组件引用来自内部编译器而不是直接在当​​前正在执行的组件。

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<int>" );
Console.WriteLine( typeof(System.Collections.Generic.List<int>) == t );
//RETURNS TRUE