2009-10-10 14 views
18

Tengo un ensamblaje (cargado como ReflectionOnly) y quiero encontrar todos los espacios de nombres en este ensamblado para poder convertirlos en "using" ("Imports" en VB) declaraciones para una plantilla de archivo de código fuente autogenerada.Encontrar todos los espacios de nombres en un ensamblado usando Reflection (DotNET)

Idealmente me gustaría limitarme a los espacios de nombres de primer nivel solamente, así que en vez de:

using System; 
using System.Collections; 
using System.Collections.Generic; 

tan solo te obtener:

using System; 

me di cuenta que es una propiedad Espacio de nombres en la clase System.Type, pero ¿hay una forma mejor de recopilar espacios de nombres dentro de un ensamblado que no implique iterar sobre todos los tipos y descartar cadenas de espacio de nombres duplicadas?

Muy agradecido, David

+1

En caso de que no se les notifica - hay un nuevo comentario debajo de mi respuesta acerca de cómo utilizar LINQ en .NET 2.0. –

Respuesta

29

No, no hay atajos para esto, aunque LINQ lo hace relativamente fácil. Por ejemplo, en C# del crudo "conjunto de espacios de nombres" sería:

var namespaces = assembly.GetTypes() 
         .Select(t => t.Namespace) 
         .Distinct(); 

Para obtener el espacio de nombres de nivel superior en lugar probablemente debería escribir un método:

var topLevel = assembly.GetTypes() 
         .Select(t => GetTopLevelNamespace(t)) 
         .Distinct(); 

... 

static string GetTopLevelNamespace(Type t) 
{ 
    string ns = t.Namespace ?? ""; 
    int firstDot = ns.IndexOf('.'); 
    return firstDot == -1 ? ns : ns.Substring(0, firstDot); 
} 

Me intriga que se por qué solo necesitas espacios de nombres de nivel superior ... parece una limitación extraña.

+3

Tenga en cuenta que el espacio de nombre puede ser nulo; tal vez algún nulo-coalescente/filtrado. Pero de lo contrario ... maldita sea, me has vencido (otra vez) ;-p –

+1

Buena llamada en la nulidad. Veo que hemos entendido la restricción de "nivel superior solamente" de manera diferente, fíjate. –

+0

No, simplemente lo codifiqué mal (vea el comentario en la publicación eliminada) - mi versión solo habría funcionado para espacios de nombres de alto nivel con un tipo. –

4

Los espacios de nombres son realmente sólo una convención de nomenclatura de nombres de tipos, por lo que sólo "existe" como un patrón que se repite a través de muchos nombres de los tipos cualificados. Entonces tienes que recorrer todos los tipos. Sin embargo, el código para esto se puede escribir probablemente como una sola expresión de Linq.

+0

Gracias Earwicker. Linq está fuera de su alcance (sigue trabajando en DotNET 2.0), pero iterar en todos los tipos solo requiere unas 20 líneas de código en non-linq. –

+1

Debería echar un vistazo a BclExtras - http://code.msdn.microsoft.com/BclExtras - proporciona definiciones para el atributo de método de extensión y la mayoría de las extensiones 'IEnumerable', en bloques compilados de forma condicional. De modo que puede usar cualquiera de los códigos de Linq en estas respuestas y, aun así, dirigirse a .NET 2.0. –

1

No tendrá más remedio que iterar sobre todas las clases.

Tenga en cuenta que las importaciones no funcionan recursivamente. "using System" no importará ninguna clase desde espacios de subnombres como System.Collections o System.Collections.Generic, sino que debe incluirlos a todos.

+0

Gracias a CodyManix, creo que ofreceré una opción que también permite la inclusión recursiva del espacio de nombres.Con la mayoría de los ensamblajes adicionales no es gran cosa, ya que no tienen tantos espacios de nombres de todos modos. –

2

Aquí hay una especie de forma lineal, todavía en esencia está itterating sobre cada elemento, pero el código es mucho más limpio.

var nameSpaces = from type in Assembly.GetExecutingAssembly().GetTypes() 
       select type.Namespace; 
nameSpaces = nameSpaces.Distinct(); 

También si su código de generación automática, que puede ser mejor para calificar totalmente todo, entonces usted no tendrá que preocuparse por los conflictos de nombres en el código generado.

+0

@Josh, gracias por la muestra. El código se genera automáticamente pero luego se expone al usuario. Así que no quiero contaminar la fuente con cientos de importaciones y usar declaraciones. Pero dado que el ensamblaje agregado típico solo tiene unos pocos espacios de nombres, creo que probablemente sea una buena idea tener la opción de incluirlos a todos. –

2

¿Un poco de LINQ?

var qry = (from type in assembly.GetTypes() 
      where !string.IsNullOrEmpty(type.Namespace) 
      let dotIndex = type.Namespace.IndexOf('.') 
      let topLevel = dotIndex < 0 ? type.Namespace 
       : type.Namespace.Substring(0, dotIndex) 
      orderby topLevel 
      select topLevel).Distinct(); 
foreach (var ns in qry) { 
    Console.WriteLine(ns); 
} 
1
public static void Main() { 

    var assembly = ...; 

    Console.Write(CreateUsings(FilterToTopLevel(GetNamespaces(assembly)))); 
} 

private static string CreateUsings(IEnumerable<string> namespaces) { 
    return namespaces.Aggregate(String.Empty, 
           (u, n) => u + "using " + n + ";" + Environment.NewLine); 
} 

private static IEnumerable<string> FilterToTopLevel(IEnumerable<string> namespaces) { 
    return namespaces.Select(n => n.Split('.').First()).Distinct(); 
} 

private static IEnumerable<string> GetNamespaces(Assembly assembly) { 
    return (assembly.GetTypes().Select(t => t.Namespace) 
      .Where(n => !String.IsNullOrEmpty(n)) 
      .Distinct()); 
} 
Cuestiones relacionadas