簡體   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