如何最好地在C#中实现可公开访问的常量

在C#中实现可公开访问的常量似乎有三种选择。 我很好奇是否有任何理由选择其中一个或者只是个人偏好。

选择1 – 私人领域加上财产吸气剂

private const string _someConstant = "string that will never change"; public string SomeConstant { get { return _someConstant; } } 

选择2 – 仅限属性吸气剂

 public string SomeConstant { get { return "string that will never change"; } } 

选择3 – 仅限公共领域

 public const string SomeConstant = "string that will never change"; 

你推荐哪个,为什么?


更新

显然,这已成为讨论是否使用conststatic readonly 。 不完全是我的意图,但它确实告诉我,选择3绝对是个坏主意,因为如果const的值在未来版本中发生变化,则需要重新编译所有引用程序集。

但是,我认为还没有人真正讨论过选择2。 如果只有一个getter返回一个值而没有别的东西,我仍然很好奇。

选择1和2实际上是等价的。

在我看来,实际上有三种不同的情况:

  • 你肯定知道字符串永远不会改变。 在这种情况下,使它成为const是合理的。 (例如, Math.PI是const。这不会很快改变。)在使用static readonly ,这有一些微妙的内存含义,但它们不太可能影响你。 如果值可能会发生变化您不想在这种情况下重新编译所有调用者,则不应该这样做,原因在于其他地方给出的原因。 请注意,对于许多项目(特别是内部企业项目),重新编译所有调用者并不是一个问题。

  • 您认为字符串将来可能会发生变化,但您知道它在任何一个版本中始终是一个常量。 在这种情况下, public static readonly字段是可以的。 请记住,使用字符串来执行此操作是很好的,因为它们是不可变的,但您不应该使用任何可变类型(如数组)执行此操作。 (公开不可变集合或使用属性并每次返回一个新副本。)

  • 您认为字符串可能会更改,甚至可能在程序的生命周期内更改…例如,“当前日期,格式化”。 在这种情况下,请使用公共静态只读属性 (仅具有getter的属性 )。 请注意,从只读字段更改为只读属性是兼容的更改,但不是二进制兼容的更改 – 因此,如果您已经满足于我的第二个项目但需要更改为第三个项目,则需要重新编译一切。

考虑

 public static readonly string myVar = "something"; 

原因:当您公开(然后在其他地方使用) constconst嵌入在消费类型的元数据中。

一个public static readonly不是,因为它是static readonly ,它只需要你实例化一次,并且它像const一样是不可变的。

正确的选择是#4:

  public static readonly string SomeConstant = "string that might change in a new version"; 

使用只读字段而不是公共const很重要。 const的文字值被编译到IL中。 如果更改const值并重新编译使用它的一个程序集,那么现在您将与使用const的其他程序集不匹配。 那些其他程序集仍将使用旧的const值运行。 很难诊断。

对于只读字段,这不会发生,其他程序集将始终读取更新的值。 如果使用const,请务必始终将它们设为私有。

const成员是类成员,而不是实例成员(换句话说, const表示static )。

如果我可以投票 – 我会投票赞成Jon Skeet的回答。 要添加它,你的#1和#2完全相同,如IL所示:

 .method public hidebysig specialname instance string get_SomeConstant() cil managed { // Code size 11 (0xb) .maxstack 1 .locals init ([0] string CS$1$0000) IL_0000: nop IL_0001: ldstr "string that will never change" IL_0006: stloc.0 IL_0007: br.s IL_0009 IL_0009: ldloc.0 IL_000a: ret } // end of method Class1::get_SomeConstant 

选项#2:

 .method public hidebysig specialname instance string get_SomeConstant() cil managed { // Code size 11 (0xb) .maxstack 1 .locals init ([0] string CS$1$0000) IL_0000: nop IL_0001: ldstr "string that will never change" IL_0006: stloc.0 IL_0007: br.s IL_0009 IL_0009: ldloc.0 IL_000a: ret } // end of method Class2::get_SomeConstant 

现在看看选项#3。 3号与#1和#2非常不同。 其原因是,如前所述,#3是静态的,因为const是静态的。 现在真正的问题是比较苹果和苹果,如果#1和#2在哪里静态存取? 然后他们将与#3更具可比性。 目前,您必须初始化选项1和2的类,但不是#3。 因此在这种情况下有一个不必要的对象初始化,你总是希望尽可能使用静态,因为要避免这种情况。

现在让我们看一下IL中的数字3:

 .field public static literal string SomeConstant = "string that will never change" 

所以,为了提高效率,我会使用#3。 这也是我多年来为许多有才华的同龄人所教授的。

现在,解决房间里的白象。 readonly和const的不同之处在于cont在编译时发生,readonly在运行时发生。 静态只读初始化一次,而非静态只读初始化每个实例一次。 例如,如果你要求你的问题做一些事情,比如为永远不会改变的错误消息创建一个const字符串类,那么使用选项#3而不是readonly static或其他。 考虑尝试在运行时而不是在编译时初始化数百条错误消息,您将看到明显的性能差异。 此外,由于你清楚地说明它是一个“永远不会改变的字符串”,在这种情况下甚至不应该考虑因为“……它永远不会改变”。 Const和ReadOnly有它们的位置,但readonly不适用于永远不会改变并且在编译时已知的项目。

属性似乎是更好的选择,因为返回的string不会嵌入元数据中。 const实际上是供内部使用的(例如,可以将版本号设置为const ,并且可以更改)或者对于引用它的代码绝对永远不会改变的值。