C# 反射和自动生成类型
我有一个类,它有一个使用“yield”返回语句的方法。将自动创建嵌套类型。使用绑定标志设置为C# 反射和自动生成类型,c#,vb.net,system.reflection,yield-return,C#,Vb.net,System.reflection,Yield Return,我有一个类,它有一个使用“yield”返回语句的方法。将自动创建嵌套类型。使用绑定标志设置为BindingFlags.DeclaredOnly的反射,我得到以下输出: //我班的公众成员。 Test.FileSystemObject..ctor Test.FileSystemObject.GetFiles(DirectoryInfo目录) Test.FileSystemObject.GetFiles(字符串路径) //自动生成的嵌套类。 Test.FileSystemObject+d\u 4..
BindingFlags.DeclaredOnly
的反射,我得到以下输出:
//我班的公众成员。Test.FileSystemObject..ctor
Test.FileSystemObject.GetFiles(DirectoryInfo目录)
Test.FileSystemObject.GetFiles(字符串路径)
//自动生成的嵌套类。
Test.FileSystemObject+d\u 4..ctor
Test.FileSystemObject+d\u4.3\uu目录
Test.FileSystemObject+d_u4.4_u
Test.FileSystemObject+d\u 4.5\u 7
Test.FileSystemObject+d_u4.5_u8
Test.FileSystemObject+d_u4.5_u6
Test.FileSystemObject+d\u 4.5\u 9
Test.FileSystemObject+d__4.5__5
Test.FileSystemObject+d__4.0目录
如何确定
assembly.GetTypes(BindingsFlags)
返回的类型是否是自动生成的类型?我正在寻找一种简单的方法来排除这些问题。您可以测试该类型是否具有[CompilerGenerated]
属性:
if (type.GetCustomAttribute(typeof(CompilerGeneratedAttribute), true) != null)
{
...
}
或者,您可以检查名称是否包含在用户代码中无效的字符。您可以在运行时编写代码,使用CSharpCodeProvider()进行编译。compileasemblyFromSource()并在当前程序集域中注册您的类型。 只要域存在,它就会驻留在那里。从结果中调用“get”访问器会自动将“Load”方法从已编译的程序集调用到当前应用程序域中 您还可以使用创建您的类型。此外,您可以强制属性标志显示为编译器生成的或此处的其他属性
var infoConstructor = typeof(CompilerGeneratedAttribute).GetConstructor(Type.EmptyTypes);
typeBuilder.SetCustomAttribute(infoConstructor, new byte[] { });
下面是我今天工作的一个例子。它的目的不仅仅是为了可视化,而是作为所有逆向工程类代码的自动生成实体集合。似乎比使用带T4和XML输出转储的文件I/O要好。尽管如此,让X-Doc/oxygen自动生成HTMLWiki页面,并在下一个PDB构建中使用和编译代码,仍然是一个可行的选择。不是一个臃肿的粉丝,请继续思考
/// <summary>
/// CreateType
/// </summary>
/// <param name="obj"></param>
/// <param name="name"></param>
/// <param name="properties"></param>
/// <param name="accessor"></param>
/// <param name="hasSubTypes"></param>
/// <returns>The newly created type of the object.</returns>
internal static Type CreateType(this Mirror obj, string name, IEnumerable<string> properties, string accessor = "", bool hasSubTypes = false) {
Type subTypeRef = null;
// Tested Regex @ http://regex101.com
const string subTypes = @"(?:<|(?:\$))([a-zA-Z_]+[0-9`]*)(?:>([a-zA-Z_]+[0-9`]*))";
var match = Regex.Match(name, subTypes);
if (match.Success) {
var refType = match.Groups[1].Value; // Class reference type.
if (match.Groups[2].Success && !string.IsNullOrEmpty(match.Groups[2].Value))
accessor = match.Groups[2].Value; // Class accessor.
// ReSharper disable once TailRecursiveCall
var enumerable = properties as IList<string> ?? properties.ToList();
subTypeRef = CreateType(obj, refType, enumerable, accessor, true);
// Tokenize this for the actual derived class name.
name = name.Substring(0, name.IndexOf('+'));
}
// Check if formating of the class name matches traditional valid syntax.
// Assume at least 3 levels deep.
var toks = name.Split(new[] { '+' }, StringSplitOptions.RemoveEmptyEntries);
Type type = null;
foreach (var tok in toks.Reverse()) {
var o = obj.RefTypes.FirstOrDefault(t => t.Value.Name == tok);
if (!o.Equals(default(KeyValuePair<string, Type>)))
continue;
// Not exists.
var sb = new StringBuilder();
sb.Append(@"
using System;
using System.Runtime.CompilerServices;
using System.Collections.Generic;
using System.Linq;
namespace HearthMirror.TypeBuilder {
[CompilerGenerated]
public class ").Append(tok).AppendLine(@" {");
if (subTypeRef != null)
sb.AppendLine($" public {subTypeRef.Name} {accessor}").AppendLine(" { get; set; }");
sb.Append(" }\n}");
var asm = RuntimeCodeCompiler.CompileCode(sb.ToString());
type = asm.GetType($"{MethodBase.GetCurrentMethod().ReflectedType?.Namespace}.{tok}"); // => generated
// Register our type for reference. This container will handle collisions and throw if encountered.
obj.RefTypes.Add(tok, type);
}
return type;
}
//
///创建类型
///
///
///
///
///
///
///新创建的对象类型。
内部静态类型CreateType(此镜像对象,字符串名称,IEnumerable属性,字符串访问器=”,bool hasSubTypes=false){
类型subtyref=null;
//测试正则表达式@http://regex101.com
常量字符串子类型=@“(?:([a-zA-Z!]+[0-9`]*)”;
var match=Regex.match(名称、子类型);
如果(匹配成功){
var refType=match.Groups[1].Value;//类引用类型。
if(match.Groups[2].Success&&!string.IsNullOrEmpty(match.Groups[2].Value))
accessor=match.Groups[2].Value;//类访问器。
//ReSharper禁用一次TailRecursiveCall
var enumerable=作为IList的属性??properties.ToList();
SubsubRef=CreateType(obj,refType,可枚举,访问器,true);
//将其标记为实际的派生类名。
name=name.Substring(0,name.IndexOf(+');
}
//检查类名的格式是否与传统的有效语法匹配。
//假设至少有3层深。
var toks=name.Split(新[]{'+'},StringSplitOptions.RemoveEmptyEntries);
Type=null;
foreach(toks.Reverse()中的var tok){
var o=obj.RefTypes.FirstOrDefault(t=>t.Value.Name==tok);
如果(!o.Equals(默认值(KeyValuePair)))
继续;
//不存在。
var sb=新的StringBuilder();
某人附加(@)
使用制度;
使用System.Runtime.CompilerServices;
使用System.Collections.Generic;
使用System.Linq;
命名空间HearthMirror.TypeBuilder{
[编译生成]
公共类“).Append(tok.AppendLine(@“{”);
if(subsubscref!=null)
sb.AppendLine($“public{submitref.Name}{accessor}”).AppendLine({get;set;}”);
sb.追加(“}\n}”);
var asm=RuntimeCodeCompiler.CompileCode(sb.ToString());
type=asm.GetType($“{MethodBase.GetCurrentMethod().ReflectedType?.Namespace}.{tok}”);//=>已生成
//注册我们的类型以供引用。如果遇到冲突,此容器将处理冲突和抛出。
对象参照类型添加(tok,类型);
}
返回类型;
}
//
///编译代码
///
///
///
公共静态程序集编译码(字符串代码){
var provider=新的CSharpCodeProvider();
var compilerparams=new CompilerParameters{GenerateExecutable=false,GenerateInMemory=true,IncludeDebugInformation=true};
foreach(AppDomain.CurrentDomain.GetAssemblys()中的变量程序集){
试一试{
var位置=程序集位置;
如果(!string.IsNullOrEmpty(位置))
compilerparams.ReferencedAssembly.Add(位置);
}捕获(不支持异常){
//这种情况发生在动态程序集上,所以忽略它。
}
}
var results=provider.compileAsemblyFromSource(编译器参数,代码);
if(results.Errors.HasErrors){
var errors=new StringBuilder(“编译器错误:\r\n”);
foreach(结果中的编译器错误。错误)
errors.AppendFormat(“行{0},{1}\t:{2}\n”、error.Line、error.Column、error.ErrorText);
抛出新异常(errors.ToString());
}
AppDomain.CurrentDomain.Load(results.CompiledAssembly.GetName());
返回results.CompiledAssembly;
}
具体来说,我认为C编译器生成的所有类型都包含
。虽然这显然是一个实现细节。@svick:细节在这里,是的,它们可能会发生变化:@Thomas Levesque:我测试了你的代码,它工作了。但是,使用ReflectionOnlyLoadFrom(路径)加载程序集时引发了异常。快速搜索后,使用CustomAttri
/// <summary>
/// CreateType
/// </summary>
/// <param name="obj"></param>
/// <param name="name"></param>
/// <param name="properties"></param>
/// <param name="accessor"></param>
/// <param name="hasSubTypes"></param>
/// <returns>The newly created type of the object.</returns>
internal static Type CreateType(this Mirror obj, string name, IEnumerable<string> properties, string accessor = "", bool hasSubTypes = false) {
Type subTypeRef = null;
// Tested Regex @ http://regex101.com
const string subTypes = @"(?:<|(?:\$))([a-zA-Z_]+[0-9`]*)(?:>([a-zA-Z_]+[0-9`]*))";
var match = Regex.Match(name, subTypes);
if (match.Success) {
var refType = match.Groups[1].Value; // Class reference type.
if (match.Groups[2].Success && !string.IsNullOrEmpty(match.Groups[2].Value))
accessor = match.Groups[2].Value; // Class accessor.
// ReSharper disable once TailRecursiveCall
var enumerable = properties as IList<string> ?? properties.ToList();
subTypeRef = CreateType(obj, refType, enumerable, accessor, true);
// Tokenize this for the actual derived class name.
name = name.Substring(0, name.IndexOf('+'));
}
// Check if formating of the class name matches traditional valid syntax.
// Assume at least 3 levels deep.
var toks = name.Split(new[] { '+' }, StringSplitOptions.RemoveEmptyEntries);
Type type = null;
foreach (var tok in toks.Reverse()) {
var o = obj.RefTypes.FirstOrDefault(t => t.Value.Name == tok);
if (!o.Equals(default(KeyValuePair<string, Type>)))
continue;
// Not exists.
var sb = new StringBuilder();
sb.Append(@"
using System;
using System.Runtime.CompilerServices;
using System.Collections.Generic;
using System.Linq;
namespace HearthMirror.TypeBuilder {
[CompilerGenerated]
public class ").Append(tok).AppendLine(@" {");
if (subTypeRef != null)
sb.AppendLine($" public {subTypeRef.Name} {accessor}").AppendLine(" { get; set; }");
sb.Append(" }\n}");
var asm = RuntimeCodeCompiler.CompileCode(sb.ToString());
type = asm.GetType($"{MethodBase.GetCurrentMethod().ReflectedType?.Namespace}.{tok}"); // => generated
// Register our type for reference. This container will handle collisions and throw if encountered.
obj.RefTypes.Add(tok, type);
}
return type;
}
/// <summary>
/// CompileCode
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
public static Assembly CompileCode(string code) {
var provider = new CSharpCodeProvider();
var compilerparams = new CompilerParameters { GenerateExecutable = false, GenerateInMemory = true, IncludeDebugInformation = true };
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) {
try {
var location = assembly.Location;
if (!string.IsNullOrEmpty(location))
compilerparams.ReferencedAssemblies.Add(location);
} catch (NotSupportedException) {
// this happens for dynamic assemblies, so just ignore it.
}
}
var results = provider.CompileAssemblyFromSource(compilerparams, code);
if (results.Errors.HasErrors) {
var errors = new StringBuilder("Compiler Errors :\r\n");
foreach (CompilerError error in results.Errors)
errors.AppendFormat("Line {0},{1}\t: {2}\n", error.Line, error.Column, error.ErrorText);
throw new Exception(errors.ToString());
}
AppDomain.CurrentDomain.Load(results.CompiledAssembly.GetName());
return results.CompiledAssembly;
}