编译器错误CS0283表示只能将基本POD类型(以及字符串,枚举和空引用)声明为const
.有没有人有关于这种限制的理由的理论?例如,能够声明其他类型的const值(例如IntPtr)会很好.
我相信这个概念const
实际上是C#中的语法糖,它只是用文字值替换了名称的任何用法.例如,给定以下声明,在编译时对Foo的任何引用都将替换为"foo".
const string Foo = "foo";
这将排除任何可变类型,因此他们可能选择此限制而不必在编译时确定给定类型是否可变?
从C#规范,第10.4章 - 常量 :(
C#3.0规范中的10.4,2.0的在线版本中的10.3)
常量是表示常量值的类成员:可以在编译时计算的值.
这基本上表示您只能使用仅包含文字的表达式.不能使用对任何方法,构造函数(不能表示为纯IL文字)的任何调用,因为编译器无法执行该执行,因此在编译时计算结果.此外,由于没有办法将方法标记为不变量(即输入和输出之间存在一对一的映射),编译器执行此操作的唯一方法是分析IL以查看是否它取决于输入参数以外的东西,特殊情况处理某些类型(如IntPtr),或者只是禁止对任何代码的每次调用.
作为一个例子,IntPtr虽然是一个值类型,但它仍然是一个结构,而不是一个内置的文字.因此,任何使用IntPtr的表达式都需要调用IntPtr结构中的代码,这对于常量声明来说是不合法的.
我能想到的唯一合法的常量值类型示例是通过声明它来初始化为零的那个,这几乎没用.
至于编译器如何处理/使用常量,它将使用计算值代替代码中的常量名称.
因此,您具有以下效果:
对原始常量名称,声明它的类或命名空间的引用不会被编译到此位置的代码中
如果您对代码进行反编译,它将包含幻数,只是因为如上所述,常量的原始"引用"不存在,只有常量的值
编译器可以使用它来优化甚至删除不必要的代码.例如,if (SomeClass.Version == 1)
当SomeClass.Version的值为1时,实际上将删除if语句,并保持正在执行的代码块.如果常量的值不是1,则将删除整个if语句及其块.
由于常量的值被编译到代码中,而不是对常量的引用,因此如果常量的值应该改变,那么使用来自其他程序集的常量将不会以任何方式自动更新已编译的代码(它不应该!)
换句话说,使用以下方案:
程序集A包含一个名为"Version"的常量,其值为1
程序集B,包含一个表达式,该表达式从该常量分析程序集A的版本号并将其与1进行比较,以确保它可以与程序集一起使用
有人修改了程序集A,将常量的值增加到2,并重建A(但不是B)
在这种情况下,程序集B在其编译形式中仍将比较1到1的值,因为在编译B时,常量的值为1.
实际上,如果这是程序集B中程序集A中任何内容的唯一用法,则程序集B将在不依赖于程序集A的情况下进行编译.在程序集B中执行包含该表达式的代码将不会加载程序集A.
因此,常量只应用于永不改变的事物.如果它是一个可能会或将来会改变某个时间的值,并且您无法保证所有其他程序集同时重建,则只读字段比常量更合适.
所以这没关系:
public const Int32 NumberOfDaysInAWeekInGregorianCalendar = 7;
public const Int32 NumberOfHoursInADayOnEarth = 24;
虽然这不是:
public const Int32 AgeOfProgrammer = 25;
public const String NameOfLastProgrammerThatModifiedAssembly ="Joe Programmer";
编辑2016年5月27日
好的,刚刚得到一个upvote,所以我在这里重新阅读我的答案,这实际上有点错误.
现在,打算在C#语言规范的是我上面写的一切.你不应该使用不能用文字表示的东西const
.
但是你呢?嗯,是....
我们来看一下这种decimal
类型.
public class Test { public const decimal Value = 10.123M; }
让我们看一下使用ildasm 查看这个类的真实情况:
.field public static initonly valuetype [mscorlib]System.Decimal X .custom instance void [mscorlib]System.Runtime.CompilerServices.DecimalConstantAttribute::.ctor(int8, uint8, uint32, uint32, uint32) = ( 01 00 01 00 00 00 00 00 00 00 00 00 64 00 00 00 00 00 )
让我为你分解一下:
.field public static initonly
对应于:
public static readonly
那是对的,const decimal
实际上是一个readonly decimal
.
这里真正的优点是编译器将使用它DecimalConstantAttribute
来发挥其魔力.
现在,这是我用C#编译器知道的唯一这样的魔法,但我认为值得一提.