2009-10-10 2 views
18

Ho un assembly (caricato come ReflectionOnly) e voglio trovare tutti gli spazi dei nomi in questo assembly in modo che io possa convertirli in "using" ("Imports" in VB) per un modello di file di codice sorgente generato automaticamente.Trovare tutti i namespace in un assembly usando Reflect (DotNET)

Idealmente vorrei limitarmi a spazi dei nomi di primo livello solo, così invece di:

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

che ci si ottiene solo:

using System; 

io ci notato è una proprietà Namespace sulla classe System.Type, ma esiste un modo migliore per raccogliere Namespace all'interno di un assembly che non coinvolge l'iterazione su tutti i tipi e il culling delle stringhe dei namespace duplicate?

Molto obbligato, David

+1

Nel caso in cui non vengono informati - c'è un nuovo commento sotto la mia risposta su come utilizzare Linq su .NET 2.0. –

risposta

29

No, non esiste una scorciatoia per questo, sebbene LINQ lo renda relativamente facile. Ad esempio, in C# il "set di spazi dei nomi" raw sarebbe:

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

Per ottenere lo spazio dei nomi di livello superiore, invece probabilmente si dovrebbe scrivere un metodo:

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

Sono incuriosito da perché hai solo bisogno di spazi dei nomi di primo livello ... sembra un vincolo strano.

+3

Attento che lo spazio dei nomi può essere nullo; forse qualche coalescenza/filtraggio nullo. Ma altrimenti ... maledizione, mi hai battuto (di nuovo) ;-p –

+1

Buona chiamata sulla nullità. Vedo che abbiamo capito il vincolo del "solo livello superiore" in modo diverso, intendiamoci. –

+0

No, ho semplicemente programmato male (vedi commento sul post eliminato) - la mia versione avrebbe funzionato solo per gli spazi dei nomi di primo livello con un tipo in. –

4

namespace sono in realtà solo una convenzione di denominazione in nomi di tipo, in modo che solo "esiste" come un modello che si ripete in molti nomi di tipo qualificato. Quindi devi scorrere tutti i tipi. Tuttavia, il codice per questo può probabilmente essere scritto come una singola espressione di Linq.

+0

Grazie a Earwicker. Linq è fuori portata (funziona ancora su DotNET 2.0) ma l'iterazione su tutti i tipi richiede solo 20 linee di codice in non-linq. –

+1

Dovresti assolutamente controllare BclExtras - http://code.msdn.microsoft.com/BclExtras - fornisce definizioni per l'attributo del metodo di estensione e la maggior parte delle estensioni 'IEnumerable', in blocchi compilati condizionatamente. In questo modo è possibile utilizzare qualsiasi codice Linq in queste risposte e tuttavia continuare a utilizzare .NET 2.0. –

1

Non avrai altra scelta che iterare su tutte le classi.

Si noti che le importazioni non funzionano in modo ricorsivo. "using System" non importerà alcuna classe da subnamespace come System.Collections o System.Collections.Generic, ma dovrai includerli tutti.

+0

Grazie a CodyManix, penso che offrirò un'opzione che consente anche l'inclusione ricettiva dello spazio dei nomi.Con la maggior parte degli assembly aggiuntivi non è un grosso problema dato che non hanno comunque molti namespace. –

2

Ecco una sorta di modo linq, in sostanza sta itterando su ogni elemento ma il codice è molto più pulito.

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

Anche se il codice di generazione automatica, si potrebbe essere meglio qualificare in modo completo tutto ciò, allora non dovete preoccuparvi di conflitti tra i nomi nel codice generato.

+0

@Josh, grazie per l'esempio. Il codice viene generato automaticamente ma viene quindi esposto all'utente. Quindi non voglio inquinare la fonte con centinaia di importazioni e usare istruzioni. Ma dal momento che l'assembly tipico aggiunto ha solo alcuni namespace, penso che sia probabilmente una buona idea avere l'opzione di includerli tutti. –

2

Un po 'di 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()); 
}