c# - 为什么C#限制了可以声明为const的类型集?

标签 c# constants compiler-errors

编译器错误CS0283指示只能将基本POD类型(以及字符串,枚举和空引用)声明为const。有人对这种限制的原理有理论吗?例如,能够声明其他类型的const值(例如IntPtr)会很好。

我相信const的概念实际上是C#中的语法糖,它只是用文字值替换了名称的任何用法。例如,给定以下声明,任何对Foo的引用都将在编译时替换为“foo”。

const string Foo = "foo";

这将排除任何可变类型,因此也许他们选择了此限制,而不必在编译时确定给定类型是否可变?

最佳答案

C# specification, chapter 10.4 - Constants:
(在C#3.0规范中为10.4,在线版本为2.0中为10.3)

A constant is a class member that represents a constant value: a value that can be computed at compile time.



基本上说,您只能使用仅包含文字的表达式。不能使用对任何方法,构造函数的调用(不能用纯IL文字表示),因为编译器无法在编译时执行该执行,从而无法计算出结果。另外,由于没有方法将方法标记为不变(即,输入和输出之间存在一对一的映射),因此编译器执行此操作的唯一方法是要么分析IL以查看是否它取决于输入参数以外的其他东西,特殊情况下处理某些类型(例如IntPtr),或者只是不允许每次调用任何代码。

作为示例,IntPtr尽管是值类型,但仍然是一种结构,而不是内置文字之一。因此,任何使用IntPtr的表达式都需要调用IntPtr结构中的代码,这对于常量声明是不合法的。

我能想到的唯一合法常量值类型示例将是一个仅通过声明就用零初始化的示例,这几乎没有用。

至于编译器如何处理/使用常量,它将使用计算所得的值代替代码中的常量名称。

因此,您具有以下效果:
  • 未引用原始常量名称,在其内声明的类或 namespace ,均已编译到此位置的代码中
  • 如果您对代码进行反编译,则其中将包含魔数(Magic Number),这仅仅是因为如上所述,对常量的原始“引用”不存在,仅常量
  • 的值
  • 编译器可以使用它来优化甚至删除不必要的代码。例如,当SomeClass.Version的值为1时,if (SomeClass.Version == 1)实际上将删除if语句,并保留正在执行的代码块。如果常量的值不为1,则将删除整个if语句及其块。
  • 由于常量的值是编译到代码中的,而不是对常量的引用,因此,如果常量的值应该更改(使用其他汇编程序的常量,则不能以任何方式自动更新已编译的代码(不应更改)! )

  • 换句话说,在以下情况下:
  • 程序集A,包含一个名为“Version”的常量,其值为1
  • 程序集B,包含一个表达式,该表达式从该常量分析程序集A的版本号并将其与1进行比较,以确保它可以与程序集
  • 一起使用
  • 有人修改了程序集A,将常量的值增加到2,并重建了A(但不是B)

  • 在这种情况下,程序集B以其编译形式仍将1与1的值进行比较,因为在编译B时,常量的值为1。

    实际上,如果仅使用程序集B中程序集A的所有内容,则将在不依赖程序集A的情况下编译程序集B。在程序集B中执行包含该表达式的代码不会加载程序集A。

    因此,常量只能用于永远不会改变的事物。如果它是一个将来可能会更改的值,并且您不能保证同时重建所有其他程序集,则只读字段比常量更合适。

    这样就可以了:
  • public const Int32 NumberOfDaysInAWeekInGregorianCalendar = 7;
  • public const Int32 NumberOfHoursInADayOnEarth = 24;

  • 虽然这不是:
  • public const Int32 AgeOfProgrammer = 25;
  • public const String NameOfLastProgrammerThatModifiedAssembly =“乔程序员”;


  • 编辑2016年5月27日

    好的,刚刚收到了赞成票,所以我在这里重新阅读了我的回答,但这实际上是错误的。

    现在,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#编译器中唯一了解的魔术,但我认为值得一提。

    关于c# - 为什么C#限制了可以声明为const的类型集?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/441420/

    相关文章:

    c# - 通用接口(interface)依赖注入(inject)到工厂

    c++ - C++ 中的函数重载(const 指针)

    c++ - 我应该尽可能在 C++11 中用 'const int' 替换 'constexpr int' 吗?

    c++ - const 和非常量函数的重载如何工作?

    ios - 以编程方式实例化 UIBarButtonItem 抛出缺少参数异常

    c# - XML 字符串到 DataGridView

    c# - 将网站转换为 Html 到 Excel

    c# - Entity Framework 代码优先 + mysql System.NullReferenceException 第一次迁移

    swift - 失败的初始化器和存储的属性

    java - 运行 Spring Boot 时出错(通过字段表达的依赖关系不满足)