2009-10-10 1 views
18

У меня есть сборка (загружаемая как ReflectionOnly), и я хочу найти все пространства имен в этой сборке, чтобы я мог преобразовать их в «использование» («Импорт» в VB) для шаблона файла с автогенерированным исходным кодом.Поиск всех пространств имен в сборке с использованием Reflection (DotNET)

В идеале я хотел бы ограничить себя пространство имен верхнего уровня только, так что вместо:

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

вы только получите:

using System; 

я заметил, есть свойство пространств имен в классе System.Type, но есть ли лучший способ для сбора пространств имен внутри сборки, который не требует итерации по всем типам и отбраковки повторяющихся строк пространства имен?

Премного, Дэвид

+1

Только в случае, если вы не уведомляются - есть новый комментарий под мой ответ о том, как использовать Linq на .NET 2.0. –

ответ

29

Нет, для этого нет ярлыка, хотя LINQ делает это относительно легко. Например, в C# сырой «набор пространств имен» будет:

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

Чтобы получить пространство имен верхнего уровня, а не вы, вероятно, следует написать метод:

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

Я заинтригован, почему вам нужны только пространства имен верхнего уровня, хотя ... это кажется странным ограничением.

+3

Остерегайтесь того, что пространство имен может быть нулевым; возможно, некоторая коллизия/фильтрация. Но в противном случае ... черт побери, ты избил меня (снова) ;-p –

+1

Хороший призыв к недействительности. Я вижу, что мы понимали ограничение «только на верхнем уровне» по-разному, заметьте. –

+0

Нет, я просто закодировал это неправильно (см. Комментарий к удаленной записи). Моя версия будет работать только для пространств имен верхнего уровня с типом. –

4

Namespaces действительно просто именование в именах типа, так что они только «существовать» как шаблон, который повторяется во многих квалифицированных именах типов. Таким образом, вам нужно пройти все типы. Однако код для этого, вероятно, может быть записан как одно выражение Linq.

+0

Спасибо Earwicker. Linq находится вне досягаемости (все еще работает над DotNET 2.0), но итерация по всем типам занимает всего около 20 строк кода в нелинии. –

+1

Вы должны обязательно проверить BclExtras - http://code.msdn.microsoft.com/BclExtras - он предоставляет определения для атрибута метода расширения и большинства расширений 'IEnumerable' в условно скомпилированных блоках. Таким образом, вы можете использовать любой из кода Linq в этих ответах и ​​все же нацеливаться на .NET 2.0. –

1

У вас не будет другого выбора, кроме повторения всех классов.

Обратите внимание, что импорт не работает рекурсивно. «using System» не будет импортировать какие-либо классы из подзонных пространств, таких как System.Collections или System.Collections.Generic, вместо этого вы должны включить их все.

+0

Спасибо CodyManix, я думаю, что я предложу вариант, который также позволяет включить рекурсивное пространство имен.С большинством дополнительных сборок это не имеет большого значения, поскольку в любом случае у них не так много пространств имен. –

2

Вот своего рода linq'ish путь, он по-прежнему по сути является итерированием по каждому элементу, но код намного чище.

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

Кроме того, если ваш автомобиль генерирующего кода, вы могли бы быть лучше, чтобы полностью определить все, то вам не придется беспокоиться о конфликтах имен в сгенерированном коде.

+0

@ Josh, спасибо за образец. Код автогенерируется, но затем отображается пользователю. Поэтому я не хочу загрязнять источник с помощью сотен Импортеров и использования утверждений. Но поскольку типичная добавленная сборка имеет только несколько пространств имен, я думаю, что, вероятно, действительно неплохо иметь возможность включить их все. –

2

Немного 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()); 
}