【问题标题】:Finding all Namespaces in an assembly using Reflection (DotNET)使用反射 (DotNET) 查找程序集中的所有命名空间
【发布时间】:2010-12-05 16:08:38
【问题描述】:

我有一个程序集(作为 ReflectionOnly 加载),我想找到该程序集中的所有命名空间,以便将它们转换为自动生成的源代码文件的“使用”(VB 中的“导入”)语句模板。

理想情况下,我只想将自己限制在顶级命名空间中,所以不要:

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

你只会得到:

using System;

我注意到 System.Type 类上有一个命名空间属性,但是有没有更好的方法来收集程序集中的命名空间,而不涉及迭代所有类型和剔除重复的命名空间字符串?

非常感谢, 大卫

【问题讨论】:

  • 以防您没有收到通知 - 在我的回答下有一条关于如何在 .NET 2.0 上使用 Linq 的新评论。

标签: c# vb.net reflection assemblies namespaces


【解决方案1】:

命名空间实际上只是类型名称中的一种命名约定,因此它们只是“存在”为一种在许多限定类型名称中重复出现的模式。所以你必须遍历所有类型。但是,这方面的代码可能可以写成单个 Linq 表达式。

【讨论】:

  • 感谢 Earwicker。 Linq 遥不可及(仍在 DotNET 2.0 上工作),但在非 linq 中迭代所有类型只需要大约 20 行代码。
  • 您绝对应该查看 BclExtras - code.msdn.microsoft.com/BclExtras - 它在条件编译的块中提供了扩展方法属性和大多数 IEnumerable 扩展的定义。因此,您可以在这些答案中使用任何 Linq 代码,但仍以 .NET 2.0 为目标。
【解决方案2】:

除了遍历所有类之外,您别无选择。

请注意,导入不能递归工作。 “使用 System”不会从 System.Collections 或 System.Collections.Generic 等子命名空间导入任何类,而是必须将它们全部包含在内。

【讨论】:

  • 感谢 CodyManix,我想我会提供一个允许递归命名空间包含的选项。对于大多数附加程序集,这没什么大不了的,因为它们没有那么多命名空间。
【解决方案3】:

不,这没有捷径可走,尽管 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);
}

我很好奇为什么你只需要顶级命名空间......这似乎是一个奇怪的约束。

【讨论】:

  • 注意命名空间可以为空;也许是一些空合并/过滤。但除此之外......该死,你(再次)击败了我;-p
  • 对无效性的好呼吁。我看到我们对“仅顶级”约束的理解有所不同,请注意。
  • 不,我只是编码错误(请参阅已删除帖子的评论)——我的版本只适用于具有输入类型的顶级命名空间。
  • 感谢乔恩,有趣的方法。我只想提供顶级命名空间作为默认值,人们总是可以选择添加额外的 using 语句。
  • @JohnLBevan:问题从已经加载的程序集开始。在其他情况下,我会将程序集用于现有类型。
【解决方案4】:

这是一种类似于 linq'ish 的方式,它本质上仍然是迭代每个元素,但代码更简洁。

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

另外,如果您的代码是自动生成的,您最好完全限定所有内容,这样您就不必担心生成代码中的命名冲突。

【讨论】:

  • @Josh,感谢您的示例。代码是自动生成的,但随后会暴露给用户。所以我不想用数百个 Imports 和 using 语句污染源。但由于典型的添加程序集只有几个命名空间,我认为选择将它们全部包含在内可能确实是个好主意。
【解决方案5】:

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

【讨论】:

    【解决方案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());
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多