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

时间:2010-05-22 17:56:54

标签: c# constants

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

选择1 - 私人字段加属性getter

private const string _someConstant = "string that will never change";

public string SomeConstant
{
    get { return _someConstant; }
}

选择2 - 仅限属性getter

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返回一个值而没有其他任何东西都有任何缺点。

6 个答案:

答案 0 :(得分:10)

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

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

  • 您肯定知道字符串永远不会改变。在这种情况下,使它成为const是合理的。 (例如,Math.PI是const。这不会很快改变。)使用static readonly执行此操作会产生一些微妙的内存影响,但它们不太可能影响您。如果值可能会更改并且您不希望在该情况下重新编译所有调用方,则不应执行此操作,原因在其他地方给出。请注意,对于许多项目(特别是内部公司项目),重新编译所有呼叫者确实不是问题。

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

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

答案 1 :(得分:5)

考虑

public static readonly string myVar = "something";

原因:当您在const中公开(然后在其他地方消费)时,const会嵌入到消费类型的元数据中。

public static readonly不是,因为它是static readonly,它只需要您进行一次实例化,并且它就像const一样不可变。

答案 2 :(得分:2)

正确的选择是#4:

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

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

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

答案 3 :(得分:2)

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

答案 4 :(得分:1)

如果我可以投票 - 我会投票赞成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不适用于永远不会改变且在编译时已知的项目。

答案 5 :(得分:1)

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