【问题标题】:How to make a strongly typed reference to a namespace without picking an arbitrary type in it如何在不选择任意类型的情况下对命名空间进行强类型引用
【发布时间】:2010-07-01 18:39:00
【问题描述】:

我正在做一个使用依赖注入的项目。在注册一组接口和类时,我需要指出这些接口和类所在的命名空间。

我不喜欢提供字符串常量,主要是因为它会损害可重构性。我也不喜欢使用其中一个接口/类并获得它的名称空间。例如:

typeof(FoodStore.Fruits.IApple).Namespace

因为所有这些任意类型名称都在周围徘徊看起来很奇怪(为什么选择IApple 而不是IOrange?),只是为了分散代码的实际意义。选择哪种类型根本没有明智的规则。

我想出了以下解决方案。

在我需要引用的每个命名空间中放置一个命名空间锚类:

namespace FoodStore.Fruits
{
    /// <summary>
    ///     Serves as a type based reference to the namespace this class
    ///     is located in.
    /// </summary>
    public sealed class _NamespaceAnchor
    {
    }
}

现在我可以使用了:

typeof(FoodStore.Fruits._NamespaceAnchor).Namespace

每当我重构命名空间时,我都不必担心 DI 注册。

虽然这个解决方案满足了问题的要求,但我仍然不高兴,因为现在我有这些丑陋的空类在闲逛。我无法将它们设为internal,因为 - 显然 - 引用跨越程序集。

我的问题是:有人知道更优雅的解决方案吗?

【问题讨论】:

    标签: c# .net dependency-injection namespaces


    【解决方案1】:

    不,没有比这更好的了——据我所知,命名空间在 CLR 中并不是真正的一流概念。是的,Type 允许您向它请求命名空间 - 但命名空间实际上是为人类而非虚拟机服务的。

    请注意,与 Java 不同,名称空间级别没有访问控制。我没有检查,但我怀疑命名空间没有特定的元数据令牌。

    我想不出任何关于在执行时会影响 CLR 的命名空间。很明显,它们会影响语言编译器——但同样,这是为了人类的利益,这样我们就可以按层次组织类型,避免在每一步都指定该层次结构。

    【讨论】:

    • 如果不在命名空间级别,可以在程序集级别完成吗?我发现自己为每个程序集创建了类似的空“标记”类,仅用于 StructureMap 引导。
    • @David:程序集没有命名空间,也没有默认命名空间(在项目级别定义,只是 Visual Studio 在创建新类时使用的东西)。
    • @Sandor:但我认为问题的精神是相同的。现在我的 StructureMap 引导程序加载了诸如“y => y.Assembly(typeof(AssemblyMarker).Assembly);”之类的东西。其中 AssemblyMarker 是该程序集中的一个空类。要从各种项目中注入实现,引导程序需要引用每个项目。与原始问题类似,我只是想知道是否有更好的方法来引用它。
    • @David:的确,精神是一样的。您可以使用反射计算基本命名空间,迭代程序集中的所有类型,确定它们共有的命名空间的左侧部分。但也有一些陷阱,因为没有什么能阻止您在程序集中放置各种不连贯的命名空间,因此您可能希望在遇到异常时抛出异常。
    • @David:恐怕 C# 中没有任何东西可以为强类型引用类型提供程序集。但我怀疑在 CLR 中有 这样的想法。
    【解决方案2】:

    我在这里担心命名空间(实际上只是类名的一部分)在您的应用程序中具有功能意义。如果后来的维护工程师移动了一些类,他们可能会无意中破坏注入系统而没有意识到这一点。该错误在运行时才会显现出来,并且可能需要一段时间才能找到。

    在这种情况下,我更喜欢更明确的选择,例如使用属性。这是一种可能性:

    public class TestInterfaceAttribute : Attribute { }
    
    public interface IMyInjectableService { }
    
    [TestInterface]
    public class TestService : IMyInjectableService { }
    
    public class RealService : IMyInjectableService { }
    

    也就是说,大多数 DI 框架要么已经具有某种形式的导出元数据(以帮助解决特定用例的正确依赖关系),要么以您明确定义依赖关系解析的方式构建。

    【讨论】:

    • 我确实意识到应该保留在同一个命名空间中的类型的风险,最终进入另一个命名空间。但在我的情况下,非常连贯的类型并将一两个移动到另一个命名空间是非常不合逻辑的。您的建议可能会避免这种风险,但随后会有意外省略该属性的风险。我是约定优于配置的忠实拥护者,我认为将兄弟类(在某些条件下)保持在同一个命名空间中是一个明智的约定。
    • @Sandor,如果是这样的话,我真的很喜欢你使用锚类的方法,因为这清楚地表明你的命名空间具有值得考虑的特殊含义。如果我必须在 2 年后维护你的代码,那么看到那个锚类会让我在移动东西时三思而后行。锚点也是一个方便的地方,可以方便地附加任何以后可能对您的命名空间有用的元数据,无论它在您的系统中扮演什么角色(也许是用于注入的类的“类别”?)锚点上的“用法”将您带到注入代码。
    • 很好的论据,尤其是关于 VS 中的“Go To Usage”功能。
    猜你喜欢
    • 1970-01-01
    • 2019-06-28
    • 2020-04-10
    • 1970-01-01
    • 2021-12-05
    • 2019-04-19
    • 2014-10-09
    • 2013-03-25
    • 2016-10-08
    相关资源
    最近更新 更多