【问题标题】:Reflection and autogenerated types反射和自动生成的类型
【发布时间】:2013-04-27 07:19:24
【问题描述】:

我有一个使用“yield”返回语句的单一方法的类。自动创建嵌套类型。使用绑定标志设置为BindingFlags.DeclaredOnly 的反射,我得到以下输出:

// 我班级的公共成员。
Test.FileSystemObject..ctor
Test.FileSystemObject.GetFiles(DirectoryInfo 目录)
Test.FileSystemObject.GetFiles(字符串路径)

// Auto generated nested class.  
Test.FileSystemObject+<GetFiles>d__4..ctor  
Test.FileSystemObject+<GetFiles>d__4.<>3__directory  
Test.FileSystemObject+<GetFiles>d__4.<>4__this  
Test.FileSystemObject+<GetFiles>d__4.<directories>5__7  
Test.FileSystemObject+<GetFiles>d__4.<files>5__8  
Test.FileSystemObject+<GetFiles>d__4.<FSO>5__6  
Test.FileSystemObject+<GetFiles>d__4.<i>5__9  
Test.FileSystemObject+<GetFiles>d__4.<unprocessed>5__5  
Test.FileSystemObject+<GetFiles>d__4.directory

如何确定assembly.GetTypes(BindingsFlags) 返回的类型是否是这种自动生成的类型?我正在寻找一种简单的方法来排除这些。

【问题讨论】:

  • @Thomas Levesque:我测试了你的代码并且它有效。但是,当使用 ReflectionOnlyLoadFrom(path) 加载程序集时会引发异常。快速搜索后,使用 CustomAttributeData.GetCustomAttribute(type) 并将每个 attribute.ToString() 与“[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]”进行比较就可以了。谢谢。

标签: c# vb.net system.reflection yield-return


【解决方案1】:

您可以在运行时编写代码以使用CSharpCodeProvider().CompileAssemblyFromSource() 进行编译,并在当前程序集域中注册您的类型。 只要域存在,它就会驻留在那里。从结果中调用“get”访问器会自动将“Load”方法从已编译的程序集调用到当前应用程序域中。

您也可以使用Reflection.Emit.TypeBuilder.CreateType() 创建您的类型。此外,您还可以强制属性标志显示为编译器生成或此处的其他属性。

var infoConstructor = typeof(CompilerGeneratedAttribute).GetConstructor(Type.EmptyTypes);

typeBuilder.SetCustomAttribute(infoConstructor, new byte[] { });

这是我今天为HearthStone Deck Tracker 工作的一个示例。它的目的更多地只是为了视觉目的,作为所有类代码逆向工程的自动生成的实体集合。似乎比使用带有 T4 和 XML 输出转储的文件 I/O 更好。虽然,这可能仍然是一个可行的选择,然后让 X-Doc/oxygen 自动生成 HTML wiki 页面,并在 PDB 的下一次构建中使用和编译代码。不喜欢过时软件,请继续使用 Reflection。

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

【讨论】:

    【解决方案2】:

    您可以测试该类型是否具有[CompilerGenerated] 属性:

    if (type.GetCustomAttribute(typeof(CompilerGeneratedAttribute), true) != null)
    {
        ...
    }
    

    或者,您可以检查名称是否包含在用户代码中无效的字符。

    【讨论】:

    • 具体来说,我相信C#编译器生成的所有类型都包含&lt;和&gt;。虽然这显然是一个实现细节。
    • @svick:详细信息在这里,是的,它们可能会发生变化:stackoverflow.com/questions/2508828/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-07
    • 2014-09-05
    • 2022-11-03
    • 1970-01-01
    相关资源
    最近更新 更多