Question

I have a class with a single method that uses a "yield" return statement. A nested type is automatically created. Using reflection with binding flags set to BindingFlags.DeclaredOnly, I get this output:

// Public members from my class.
Test.FileSystemObject..ctor
Test.FileSystemObject.GetFiles(DirectoryInfo directory)
Test.FileSystemObject.GetFiles(String path)

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

How can I determine whether a type returned by assembly.GetTypes(BindingsFlags) is such an auto generated type? I'm looking for a simple way to exclude these.

Was it helpful?

Solution

You can test if the type has the [CompilerGenerated] attribute:

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

Alternatively, you can check if the name contains characters that wouldn't be valid in user code.

OTHER TIPS

You can write code at run-time to be compiled using CSharpCodeProvider().CompileAssemblyFromSource() and register your type within the current assembly domain. It will reside there as long as the domain exists. Calling the 'get' accessor from the results automatically invokes the 'Load' method from the compiled assembly into the current application domain.

You can also use Reflection.Emit.TypeBuilder.CreateType() to create your type. Moreover, you can force the attribute flag to show as compiler generated or other attributes within here as well.

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

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

Here is an example of what I am working on today for HearthStone Deck Tracker. It's purpose is more of just for visual purposes as an auto-generated entity collection of all the class code reverse engineered. Seems to be better than using file I/O w/ T4 and XML output dumps. Although, that might be a viable option still to then have X-Doc/oxygen auto-generate HTML wiki pages with and having the code avail to be used and compiled in on next build for the PDBs. Not a fan of bloatware, stay with 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;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top