【问题标题】:What's the best way to implement a global constant in C#?在 C# 中实现全局常量的最佳方法是什么?
【发布时间】:2011-12-09 13:51:59
【问题描述】:

我有一个 Common 项目,我在其中添加了 QueryStringNames 的公共常量。

我知道通常常量应该是内部的或私有的,但我在这里需要公共常量,因为我想允许全局访问查询字符串名称、会话密钥等。

我知道有 3 个解决方案,但它们都有一个重要问题。调用者程序集将包含我的常量的副本,这意味着如果我必须更改一个常量值,我将不得不编译我的通用程序集和调用者程序集!

1) public const string ConstName = "a value";
2) public readonly string ConstName = "a value";
3) To be stored in a public resource file.

除了将公共常量存储在 web.config 文件(没有智能感知)中之外,在 C# 中定义公共常量的最佳方法是什么?

【问题讨论】:

  • 如果您创建了正确的 xsd 架构,您可以在配置中使用智能感知
  • 我有点困惑。值的存储位置(即 webconfig)与内存中该值的副本的可访问性有什么关系?
  • 你是对的@asawyer;如果再次使用 const 和 readonly 将无法解决问题。
  • @asawyer - 我认为 OP 的观点是,如果它在配置中,他就不必重新编译所有程序集?
  • 如果你的意思是这样的话,你不能在没有它的类实例的情况下使用常量。我为此使用了一个静态 co 类,所以我使用 co.PI,所以它只比在代码中使用 PI 长 3 个字符。

标签: c# asp.net resources constants readonly


【解决方案1】:

这取决于。如果它确实是一个不会改变的常量,即使在您的代码的未来版本中,那么const 也可以。否则使用static readonly 字段。

const 将嵌入到调用程序集中,而 static readonly 调用程序集仅包含对该字段的引用。这意味着const 需要在您更改值时重新编译所有相关代码,而public readonly 即使不重新编译调用程序集也会使用新值。

如果您想将“常量”存储在配置文件中,但像​​ Intellisense 一样,您可以使用没有公共设置器的属性。然后在运行时从配置文件中填充它。但我认为配置值首先不应该是static。对于配置值,我会使用某种单例,最好是 IoC 变体,而不是 Class.Instance 变体。所以我只需定义一个如下所示的接口:

interface IMyConfig
{
  string Key{get;}
}

并让需要此配置的类将其作为构造函数参数:

public MyClass(IMyConfig config)
{
    ...
}

【讨论】:

  • 为什么?!我测试过,静态只读也需要重新编译调用者。
  • 我很确定更改静态只读字段不需要重新编译调用者。调用程序集仅包含您的字段的名称,而不是它的值。
  • @CodeInChaos 是正确的。如果您要使用常量,则该值由编译器插入,而只读字段在运行时插入(在声明时或在构造函数中,取决于您如何实现只读值)。每当初始化类的静态字段时,即使是静态只读也会在运行时完成。
  • 我同意,如果它们有可能改变(并导致重新编译),那么它们并不是真正的常量。我更喜欢 const 因为它们更容易在 switch / case 语句中使用。但是,如果您谈论的是查询字符串和会话密钥,那么更改它们无论如何都会导致重新编译和重新部署。
  • @William 也许你对 VS 默认重建所有依赖程序集感到困惑。但是,如果您采用新的“静态只读”包含程序集并简单地将单个 dll 替换为不同的版本,则在其上构建的所有程序集应该仍然可以工作,并使用新值。
【解决方案2】:

如果您认为要更改它并且担心必须编译它,那么为什么不在 web 配置文件中使用 appSettings 呢?这就是它的用途。如果您真的需要智能感知,那么您可以将一个类放入其中一个程序集中,该程序集读取配置值并将其作为属性公开,以便于引用。如果它是敏感数据,那么我不会把它放在配置文件中,我只会编译它,因为你不想破坏你的应用程序。

<appSettings>
    <add key="myconstant" value="here's the value!" />
</appSettings>

这是引用该值的类,它为您提供智能感知,将来可以轻松更改它,而无需重新编译任何东西

public class MyAppConfigSettings
{
    public string MyConstant { get; private set; }

    public MyAppConfigSettings()
    {
        MyConstant = ConfigurationManager.AppSettings["myconst"];
    }
}

它可能不是你解决方案的答案,但它可能会给你一些其他的想法。

【讨论】:

  • 我会重写构造函数以采用 NameValueCollection 或类似的东西,而不是从静态 AppSettings 属性中获取它。然后通过 IoC 注入。
  • 是的,我希望在不使用 web.config 的情况下有一个更简单的解决方案。我通常会编写我自己的 ConfigWrapper 和 IConfig 以及一个单例类作为我的 CustomConfigManager 以将所有这些属性设置为只读并且只与物理文件交互一次。如果需要更改,则应重新启动应用程序。那么 web.config 是避免此问题的唯一方法吗?
  • 我不确定这是否是避免它的唯一方法。我的意思是您也可以将它保存在数据库记录中。这样您就可以在需要时更改该值,而不必重新编译。不过老实说,这确实不是一个复杂的解决方案。这很简单。
  • @CodeInChaos,这也是完全可以接受的,但我的实现完全封装了从配置中读取的内容,并防止使用该类的客户端代码必须获取 NameValueCollection 才能传递它。保持松耦合。如果您需要针对配置类编写单元测试(我不知道您为什么要这样做,没有任何逻辑要测试),您可以提供第二个构造函数,它采用 NameValueCollection。我认为这样做已经结束了。
  • 您的类与配置存储机制紧密耦合。但是客户端代码不应该构造MyAppConfigSettings 的实例。构造MyAppConfigSettings 的唯一代码应该是IoC 容器。我不是在谈论测试配置类,而是关于测试依赖于配置类的类。对我来说,使用不同的配置进行测试听起来很标准。
【解决方案3】:

如果您正在激活 fxCop(Visual Studio 发行版中包含的代码分析工具),您可能会收到建议将常量更改为:

public static readonly string ConstName = "a value";

【讨论】:

  • 我手头没有编译器,但考虑到const 值的性质,static 关键字是必需的,甚至是合法的,这让我感到惊讶。
  • @spender 你指的是这个答案的早期版本吗?因为static readonly 肯定是有道理的。 static 仅在使用 const 时隐含,而在使用 readonly 时不隐含。
  • @CodeInChaos :我可能误读了答案。在发布我的评论时,我认为它在同一个声明中同时包含 conststatic
【解决方案4】:

我不确定我是否完全理解了这个问题...您要求一种解决方案来存储一些全局变量,如果您更改它们,这些全局变量不会导致重新编译到引用这些全局变量的程序集?如果是这样,那么为什么不尝试按照Inversion of Control 原则重新设计您的架构呢?想想“不要打电话给我们,我们会打电话给你”的好莱坞原则。如果所有需要一些 const 的程序集只调用一个接口(他们拥有的),该接口公开了一个具有他们需要的值的属性,那么你就有一个实现这些接口的常量项目(通过引用这些项目然后实现这些接口)那么当您更改常量的值时,这些项目将永远不需要重新编译。

我相信您无论如何都知道它们,但请阅读SOLID principles,“D”是依赖倒置原则(控制倒置)。我认为考虑到您的担忧(假设我的理解正确),他们真的可以帮助您。

控制反转的一个例子可以很简单:

MyService.dll:

public class MyService
{

    // injected dependency
    public IMyConstants MyConstants { get; set; }

    public MyMethod(){

        // get your query...
        var query = IMyConstants.Query;
    }

}

MyConstants.dll:

public MyConstants : IMyConstants {

    // implementation of query property from the myservices.dll interface
    public string Query { ... }

}

所以 myconstants.dll 引用 myservice.dll 而不是相反(意味着 myservices 不需要重新编译)。然后引导代码(设置它并注入依赖项)位于其他地方。

对不起,如果我误解了你,希望对你有所帮助!

【讨论】:

  • 你说得对,配置应该通过 IoC 注入,而不是从静态成员中读取。一些挑剔:我会使用构造函数注入,我不会在这里使用名称Constant,因为配置值并不是真正的常量。如果它真的是一个常数,我们在这里就不需要 DI。
  • 维基百科引用 SOLID 中的 I 为“接口隔离原则”。我认为您想要的原则是“推,不要拉”。
  • 是的,这听起来很对——不过你知道我的意思。我只是混淆了我认为对 DI 的一些解释!
  • 致所有阅读过我上述评论的人——我已经进行了一些编辑以包含对 SOLID 原则的正确引用。干杯@CodeInChaos!
【解决方案5】:

在大多数情况下,我更喜欢第二个选项,因为它不会引起问题(通过将值复制到其他程序集)。速度可能比常数慢,但这种纳秒级的速度还很不成熟。

【讨论】:

    【解决方案6】:

    您可以使用 Cache 对象并在 Global.asax 中定义它们

    【讨论】:

      【解决方案7】:

      如前所述,情况不一样:

      • const:是常量,只能通过重新编译才能修改。
      • 只读:值在声明或构造函数中初始化,之后保持只读状态。

      当字段声明包含只读修饰符时,对声明引入的字段的赋值只能作为声明的一部分或在同一类的构造函数中发生

      【讨论】:

      • 对于我指出的问题,它们的行为相似。除非这些值是从单独的物理文件(例如配置文件)初始化的,否则在更改的情况下将需要调用者重新编译。
      • 在你的情况下,我会在静态助手类中使用 const。
      • 你没有理解问题。
      猜你喜欢
      • 1970-01-01
      • 2011-05-24
      • 2010-10-12
      • 1970-01-01
      • 2015-07-16
      • 2013-06-18
      • 2011-12-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多