我一直在查看Type.Namespace
,Type.Name
,Type.FullName
和Type.AssemblyQualifiedName
的返回值。存在不一致之处。
对于像ConsoleApplication8.Program+InnerClass
这样的内部类,Namespace返回ConsoleApplication8
而Name
返回InnerClass
,省略Program
,因此连接Type.NameSpace
和{ {1}}将是类名的不完整表示(仅作为示例)。
即使Type.Name
属性也不一致。虽然它省略了程序集名称并为这样的内部类返回FullName
,但ConsoleApplication8.Program+InnerClass
在FullName
等类型的泛型参数中包含程序集名称(即使外部泛型省略了它键入自己,所以我猜有一定程度的一致性。)
我目前正在使用带有缓存类型名称查找的代码,该查找使用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.InnerClass
和Program
之间的加号)。问题是调用InnerClass
将不起作用,因为它需要加号,并且有时需要汇编名称。
那么如何给出一个Type.GetType(name)
对象的引用,给定一个友好的C#类名,因为它会在代码中被引用?
答案 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;
}
}
}
需要注意的一件非常重要的事情 - 您需要将编译器列表添加到您希望从中提取类型的编译器中。这意味着如果你有一个你想要引用的自定义类型,你需要将它提供给编译器,否则,这是有效的!享受!