Подтвердить что ты не робот

Поиск всех пространств имен в сборке с использованием Reflection (DotNET)

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

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

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

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

using System;

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

Значительная обязанность, Дэвид

4b9b3361

Ответ 1

Нет, для этого нет ярлыка, хотя LINQ делает это относительно легко. Например, в С# необработанный "набор пространств имен" будет выглядеть следующим образом:

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

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

Ответ 2

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

Ответ 3

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

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

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

Ответ 4

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

Ответ 5

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

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

Ответ 6

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