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

2023-11-24

编译错误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 语句及其块将被删除。
  • 由于常量的值被编译到代码中,而不是对常量的引用,因此如果常量的值发生变化(不应该发生变化!),使用其他程序集中的常量将不会以任何方式自动更新已编译的代码。

换句话说,有以下场景:

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

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

事实上,如果这是程序集 B 中程序集 A 中的任何内容的唯一用法,则程序集 B 将在不依赖于程序集 A 的情况下进行编译。在程序集 B 中执行包含该表达式的代码将不会加载程序集 A。

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

所以这是可以的:

  • 公共常量 Int32 NumberOfDaysInAWeekInGregorianCalendar = 7;
  • 公共常量 Int32 NumberOfHoursInADayOnEarth = 24;

虽然这不是:

  • 公共 const Int32 AgeOfProgrammer = 25;
  • public const String NameOfLastProgrammerThatModifiedAssembly = "乔程序员";

2016 年 5 月 27 日编辑

好的,刚刚得到了赞成票,所以我在这里重新阅读了我的答案,这实际上有点错误。

现在意图C# 语言规范的内容就是我上面写的所有内容。你不应该使用无法用文字表示的东西作为const.

但你可以吗?嗯,是....

让我们看一下decimal type.

public class Test
{
    public const decimal Value = 10.123M;
}

让我们看看这个类是什么样子的really当用 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# 编译器的唯一这样的魔力,但我认为它值得一提。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 C# 限制可以声明为 const 的类型集? 的相关文章

  • C# 中的 Culture 相当于 Java 中的 Locale 吗?

    C 使用文化的概念 这在操作上与 Java 中的 Locale 类似吗 或者底层概念是否存在显着差异 从文化而不是语言环境的角度进行工作是一种寻找正确抽象层次的尝试 从以类似方式做事的人群的角度来考虑事物 而不是谈论地理区域和语言 并有点疯
  • 如何在C编程中获取当前时间(以毫秒为单位)[重复]

    这个问题在这里已经有答案了 可能的重复 如何使用 ANSI C 测量以毫秒为单位的时间 https stackoverflow com questions 361363 how to measure time in milliseconds
  • 静态成员函数与C语言绑定?

    以下 C 代码可使用 Visual C 和 g 进行编译 struct S static void foo extern C void S foo struct T static void foo extern C void T foo a
  • C 中的分段错误

    我需要用 0 填充二维数组 但编译后的程序会出现此错误 怎么了 int main int vert 1001 1001 int hor 1001 1001 int dudiag 1416 1416 int uddiag 1416 1416
  • 无法在更新面板中找到上传的文件

    aspx
  • 使用 boost::iterator_facade<>

    我有一个链表结构 struct SomeLinkedList const char bar int lots of interesting stuff in here DWORD foo SomeLinkedList pNext 它是现有
  • 按位非运算符

    为什么要按位运算 0 打印 1 在二进制中 不是0应该是1 为什么 你实际上很接近 在二进制中 不是0应该是1 是的 当我们谈论一位时 这是绝对正确的 然而 一个int其值为0的实际上是32位全零 将所有 32 个 0 反转为 32 个 1
  • 在 DefaultHttpContext 上使用 FeatureCollection 时,响应对象为 null

    我正在测试一些 net Core 中间件 并希望使用整个 asp net Core http 管道来运行中间件 而不是模拟它 问题是 当我使用特征集合时 不知何故 响应对象没有在 httpRequest 中设置 并且它在请求本身上是只读的
  • 最小对的总和

    Given 2N点 in a 2D plane 你必须将它们分组为N pairs使得所有对的点之间的距离的总和是最小可能值 所需的输出只是总和 换句话说 如果a1 a2 an分别是第一对 第二对 和第 n 对点之间的距离 则 a1 a2 a
  • 在标准库中静态链接时如何支持动态插件?

    假设一个应用程序myapp exe是使用构建的g 它使用标志 static libstdc 这样就可以安装在没有环境的情况下libstdc so myapp exe还添加了对某些功能的插件支持plugf可以通过动态加载dlopen来自共享库
  • 使用 for 循环创建链表

    这是我的结构 struct ListItem int data struct ListItem next 假设链表的第一个节点的 data 0 我想编写一个 for 循环来创建大小为 5 的链表 但我不知道如何工作 我尝试了以下方法 int
  • C# Linq 可以做组合数学吗?

    我有这个数据结构 class Product public string Name get set public int Count get set var list new List
  • DLL 中的 XP 风格组合框

    我需要使用 C 和 WIN32 API 无 MFC 在 DLL 中创建 XP 风格的组合框 我设法在 DLL 中创建控件 不是以 XP 风格 我设法在带有清单的 exe 中创建 XP 样式组合框 但它在 DLL 中不起作用 为了让您的 DL
  • 如何最好地为 Visual Studio 2017 构建的 CMake C++ 项目设置输出目录?

    我使用 Visual Studio 2017 使用 vcxproj 文件构建 C 桌面项目 我喜欢默认行为 其中输出目录是项目下面的子目录 例如 myproj sln myproj vcxproj x64 myproj release my
  • C# 编译器编译 .txt .obj .java 文件

    using System class Program public static void Main Console WriteLine Hello World Console ReadLine 我将文件另存为1 java 2 obj an
  • 序列化时如何跳过 xml 声明?

    我正在尝试输出一个没有 xml 头的 xml 文件 例如 我试过 Type t obj GetType XmlSerializer xs new XmlSerializer t XmlWriter xw XmlWriter Create c
  • RabbitMQ + Windows + LDAP 无需发送密码

    我正在尝试在 Windows 7 上使用 RabbitMQ 3 6 2 进行 LDAP 身份验证 授权 我已经在应用程序发送用户名 密码的情况下进行了基本身份验证 但密码位于我需要弄清楚如何进行的代码中避免 有没有人在不提供密码的情况下成功
  • 在 C++ 中将大型数据向量写入/读取到二进制文件

    我有一个 C 程序 它通过将 ascii 文件中的网格人口数据读取到大型 8640x3432 元素双精度向量中来计算给定半径内的人口 将 ascii 数据读入向量大约需要 30 秒 循环每列和每行 而程序的其余部分只需要几秒钟 我被要求通过
  • 散列 hash_hmac 时,Convert.ToChar(0) 散列结果与 PHP 中的 chr(0) 不同的字符串

    我在 PHP 中有一个字符串 它被转换为字节数组并进行哈希处理 转换为字节数组的字符串如下所示 G 字符 0 便便 我需要 C 中的等效字节数组 这样我才能得到相同的哈希值 编辑 这是完整的问题 生成的哈希值不同 PHP api secre
  • “保留供任何使用”是什么意思?

    注意 这是一个c questions tagged c问题 虽然我补充说c questions tagged c 2b 2b如果某些 C 专家可以提供 C 使用与 C 不同的措辞的基本原理或历史原因 在 C 标准库规范中 我们有这个规范文本

随机推荐