2011-03-10 9 views
5

Por primera vez, he necesitado hacer un escaneo de ensamblaje manualmente. Me encontré con C# - how enumerate all classes with custom class attribute? que me creó conMejores prácticas para escanear todas las clases y métodos para el atributo personalizado

var typesWithMyAttribute = 
(from assembly in AppDomain.CurrentDomain.GetAssemblies() 
    from type in assembly.GetTypes() 
    let attributes = type.GetCustomAttributes(typeof(SomeAttribute), true) 
    where attributes != null && attributes.Length > 0 
    select new { Type = type, Attributes = attributes.Cast<SomeAttribute>() }) 
    .ToList(); 

Qué era lo suficientemente simple para expandir a nivel de método

var methodsWithAttributes = 
    (from assembly in AppDomain.CurrentDomain.GetAssemblies() 
    from type in assembly.GetTypes() 
    from method in type.GetMethods() 
    let attributes = method.GetCustomAttributes(typeof(SomeAttribute), true) 
    where attributes != null && attributes.Length > 0 
    select new { Type = type, Method = method, 
      Attributes = attributes.Cast<SomeAttribute>() }) 
    .ToList(); 

¿Debo tratar de combinar estos 2 para hacer esto en una sola exploración o ¿Es eso solo caer en la optimización temprana? (el escaneo solo se ejecutará al inicio de la aplicación)

¿Hay algo diferente que sería más óptimo que hacer para el escaneo de los métodos ya que hay muchos más métodos que tipos en los ensamblajes?

+0

Voy a apostar que la enumeración de las asambleas y tipos va a ser mucho más rápido que encontrar y crear instancias de sus atributos, de manera que el almacenamiento en caché de los conjuntos y tipos es va a ser inútil. – Gabe

Respuesta

3

La reflexión es muy lento ...

Creo que he ir a los fundamentos allí. Te recomiendo que cambies tu código ligeramente para evitar que se realice el escaneo completo adicional.

Si tiene que hacer esto más de una vez, también le recomiendo que considere almacenar en caché los resultados por el período de tiempo que sea apropiado.

Sorta como este pseudo-código:

... (optional caches) ... 
IDictionary<Type, IEnumerable<Attributes>> typeAttributeCache = new ... 
IDictionary<MethodInfo, IEnumerable<Attributes>> methodAttributeCache = new ... 

... (in another method or class) ... 
foreach assembly in GetAssemblies() 
    foreach type in assembly.GetTypes()   
    typeAttributes = typeAttributeCache.TryGet(...) // you know the correct syntax, trying to be brief 

    if (typeAttributes is null) 
     typeAttributes = type.GetCustomAttributes().OfType<TypeImLookingFor>(); 
     typeAttributeCache[type] = typeAttributes; 

    foreach methodInfo in type.GetMethods()   
     methodAttributes = methodAttributeCache.TryGet(...) // same as above 

     if (methodAttributes is null) 
     methodAttributes = methodInfo.GetCustomAttributes().OfType<TypeImLookingFor>(); 
     methodAttributeCache[type] = methodAttributes; 

    // do what you need to do 
2

Creo que puede optimizar esto, pero depende de cómo se colocan los atributos en los métodos y tipos. Si sabe que todos sus tipos y/o métodos con un atributo especial están definidos en ensamblajes particulares, puede escanear solo estos ensamblajes.

También se podría definir algunos métodos, como:

- IEnumerable<Type> GetAllTypesFromAssemblyByAttribute<TAttribute>(Assembly assembly) where TAttribute : Attribute 
- IEnumerable<MethodInfo> GetAllMethodsFromTypeByAttribute<TAttribute>(Type type) where TAttribute : Attribute 

y utilizar estos métodos en su método de exploración principal.

Así que su método de exploración resultado podría ser así:

private void ScanAndDoSmth<TAttribute>(IEnumerable<Assembly> assemblies) 
where TAttribute : Attribute 
{ 
    var result = 
     from assembly in assemblies 
     from type in GetAllTypesFromAssemblyByAttribute<TAttribute>(assembly) 
     let attributes = type.GetCustomAttributes(typeof(TAttribute), true) 
     where attributes != null && attributes.Length > 0 
     select new { Type = type, Attributes = attributes.Cast<TAttribute>(); 
} 
Cuestiones relacionadas