2013-05-02 6 views
2

У меня есть класс с единственным методом, который использует оператор возврата «yield». Вложенный тип создается автоматически. Используя отражение с обязательными флагами, установленными на BindingFlags.DeclaredOnly, я получаю этот вывод:Отражающие и автогенерированные типы

// Публичные участники из моего класса.
Test.FileSystemObject..ctor
Test.FileSystemObject.GetFiles (каталог DirectoryInfo)
Test.FileSystemObject.GetFiles (String путь)

// 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) является такой автогенерируемый тип? Я ищу простой способ исключить их.

+0

@Thomas Левеске: Я проверил ваш код и он работал. Тем не менее, это вызвало исключение, когда сборка была загружена с использованием ReflectionOnlyLoadFrom (путь). После быстрого поиска, используя CustomAttributeData.GetCustomAttribute (тип) и сравнивая каждый атрибут. ToString() - "[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]" сделал трюк. Благодарю. – bricklayer137

ответ

5

Вы можете проверить, если тип имеет атрибут [CompilerGenerated]:

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

В качестве альтернативы, вы можете проверить, если имя содержит символы, которые не должны быть допустимы в пользовательском коде.

+0

В частности, я считаю, что все типы, сгенерированные компилятором C#, содержат '<' and '>'. Хотя это, очевидно, деталь реализации. – svick

+1

@svick: подробности здесь, и да, они могут быть изменены: http://stackoverflow.com/questions/2508828/where-to-learn-about-vs-debugger-magic-names/2509524#2509524 –

0

Вы можете написать код во время выполнения для компиляции с использованием CSharpCodeProvider().CompileAssemblyFromSource() и зарегистрировать свой тип в текущем домене сборки. Он будет проживать там до тех пор, пока домен существует. Вызов «получателя» из результатов автоматически вызывает метод «Загрузить» из скомпилированной сборки в текущий домен приложения.

Вы также можете использовать Reflection.Emit.TypeBuilder.CreateType(), чтобы создать свой тип. Кроме того, вы можете заставить флаг атрибута показать как сгенерированный компилятор или другие атрибуты.

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

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

Вот пример того, что я работаю на сегодняшний день для HearthStone Deck Tracker. Это цель скорее для визуальных целей, так и для автоматически созданной коллекции сущностей всего кода класса с обратной обработкой. Кажется, это лучше, чем использование дампов вывода файлов ввода/вывода w/T4 и XML. Хотя, возможно, это был бы жизнеспособный вариант, который затем должен был генерировать HTML-вики-страницы X-Doc/кислород и использовать код и использовать его в следующей сборке для 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; 
} 
Смежные вопросы