2009-10-10 5 views
18

J'ai un assembly (chargé en tant que ReflectionOnly) et je souhaite trouver tous les espaces de noms dans cet assembly pour les convertir en "using" ("Imports" dans VB) pour un modèle de fichier de code source généré automatiquement.Recherche de tous les espaces de noms dans un assembly à l'aide de Reflection (DotNET)

Idéalement, je voudrais me limiter à namespaces haut niveau seulement, donc au lieu de:

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

vous souhaitez seulement obtenir:

using System; 

J'ai remarqué il y a une propriété Namespace sur la classe System.Type, mais existe-t-il un meilleur moyen de collecter des espaces de noms à l'intérieur d'un assembly qui n'implique pas d'itérer sur tous les types et de trier les chaînes d'espace de noms en double?

Bien obligé, David

+1

Juste au cas où vous ne sont pas informés - il y a un nouveau commentaire dans ma réponse sur la façon d'utiliser LINQ sur .NET 2.0. –

Répondre

29

Non, il n'y a pas de raccourci pour cela, bien que LINQ le rende relativement facile. Par exemple, en C# « l'ensemble des espaces de noms » brut serait:

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

Pour obtenir l'espace de noms de haut niveau plutôt que vous devriez probablement écrire une méthode:

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

Je suis intriguée à pourquoi vous n'avez besoin que d'espaces de noms de niveau supérieur ... cela semble une contrainte étrange.

+3

Attention, cet espace de noms peut être nul; peut-être un peu de coalescence/filtrage. Mais sinon ... sacrément, vous me battez (encore) ;-p –

+1

Bon appel sur la nullité. Je vois que nous avons compris différemment la contrainte "top level only". –

+0

Non, je l'ai simplement codé mal (voir le commentaire sur un post supprimé) - ma version n'aurait fonctionné que pour les espaces de noms de niveau supérieur avec un type. –

4

espaces de noms sont vraiment juste une convention de nommage dans les noms de type, de sorte qu'ils ne « exister » comme un modèle qui se répète dans de nombreux noms de type qualifiés. Donc, vous devez parcourir tous les types. Cependant, le code pour cela peut probablement être écrit comme une seule expression Linq.

+0

Merci Earwicker. Linq est hors de portée (travaille encore sur DotNET 2.0) mais l'itération sur tous les types ne prend que 20 lignes de code en non-linq. –

+1

Vous devriez certainement vérifier BclExtras - http://code.msdn.microsoft.com/BclExtras - il fournit des définitions pour l'attribut de méthode d'extension et la plupart des extensions 'IEnumerable', dans des blocs conditionnellement compilés. Vous pouvez donc utiliser n'importe quel code Linq dans ces réponses tout en ciblant toujours .NET 2.0. –

1

Vous n'aurez pas d'autre choix que d'itérer sur toutes les classes.

Notez que les importations ne fonctionnent pas récursivement. "using System" n'importera aucune classe à partir d'espaces de sous-noms comme System.Collections ou System.Collections.Generic, mais vous devrez les inclure tous.

+0

Merci CodyManix, je pense que je vais offrir une option qui permet également l'inclusion de l'espace de noms récursif.Avec la plupart des assemblages supplémentaires, ce n'est pas grave car ils n'ont pas autant d'espaces de noms de toute façon. –

2

Voici une sorte de méthode linq'ish, il est toujours en train de répéter sur chaque élément mais le code est beaucoup plus propre.

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

Aussi, si votre code de génération automatique, vous pourriez être mieux de se qualifier pleinement tout, vous ne serez pas à vous soucier de conflits de noms dans le code généré.

+0

@Josh, merci pour l'échantillon. Le code est généré automatiquement mais ensuite exposé à l'utilisateur. Je ne veux donc pas polluer la source avec des centaines d'importations et en utilisant des instructions. Mais comme l'assemblage ajouté typique ne possède que quelques espaces de noms, je pense que c'est probablement une bonne idée d'avoir la possibilité de les inclure tous. –

2

Un peu 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()); 
} 
Questions connexes