在C#中用类型推断一个常量

And*_*ech 40 c# type-inference constants

在C#中,以下类型推断有效:

var s = "abcd";
Run Code Online (Sandbox Code Playgroud)

但是,当变量是常数时,为什么不能推断出类型呢?

以下引发了编译时异常:

const var s = "abcd"; // <= Compile time error: 
                      //    Implicitly-typed local variables cannot be constant
Run Code Online (Sandbox Code Playgroud)

Eri*_*ert 33

我实际上希望Lippert流行,然后看看这个问题

如果您想引起我的注意,您可以在文本中留下我的名字 - 而不是评论 - 我最终会找到它.或者,更好的是,你可以"推特"到@ericlippert.请注意,这不构成服务级别协议; 我在业余时间这样做.

当变量是常量时,为什么不能推断出类型呢?

"常数"和"变量"是对立的. const var给我打电话的颤抖.常量是一个永远不会改变且没有存储位置的值; 变量是其内容发生变化的存储位置.它们完全不同,所以不要试图将它们结合起来.该var语法被选为喊一声"这是一个变量",我们正在与它坚持.

var可以站在一个特定类型声明,但它与合并const严重muddies的编译器什么的图片确实与价值.因此const var不允许这种混淆,你必须明确键入你的常量.

对于不使用的推断常量,我会完全没问题var:

const Pi = 3.14159;
Run Code Online (Sandbox Code Playgroud)

对我来说似乎很好.但是,我知道没有计划将其添加到C#中.

  • 但有没有一个特定的原因可以解释为什么设计者选择不对常量实现类型推断,而允许使用`var`作为已知类型的语法糖(如`var s ="abc";`)? (3认同)
  • 如果我在数学中有一个等式并且它包含一个"变量",那么这个变量通常是一个常数.例如,在"2x = 6"中,x是,并且总是将是值3.有些人会被像"x = x + 1"这样的东西打扰,这是一个无法解决的等式,但在许多编程语言中很常见,包括C#. (2认同)
  • @helium,问题在于编程语言滥用=运算符意味着"将此值分配给此存储位置",而不是用它来表示"评估此等式的真实性或虚假性",或"声明等效性这两个实体".我个人宁愿在C/C++/C#/等中使用=运算符< - 运算符,但由于历史原因,我们仍然坚持=表示赋值. (2认同)
  • @Michael:情况并不完全相同,但它们是相似的.我注意到在分析"var"字段时我们不得不担心循环定义.今天我们不得不担心常数分析的周期.如果你说const int x = y + 10 ;,那么const int y的定义最好不要依赖于x. (2认同)
  • FSharp 在 ``let x = 10.0`` 上的类型推断没有问题 rhs 表达式上的类型推断似乎是与引用的可变性正交的概念。``var`` 用于可变引用。``const`` 用于不可变引用。两者的类型推断都是有意义的。 (2认同)
  • 我觉得这在 C++ 中实现得更好,其中“auto”关键字有点类似于 C# 的“var”,并告诉编译器为声明的对象提供与“=”右侧表达式相同的类型,但是您*可以* 将 `auto` 与类型修饰符结合使用,例如 `const`、`*`(指针)和 `&amp;`(引用)。这种 `const` 与 `var` 的对抗对于习惯使用 C++ 的开发人员来说非常令人困惑,并且它使得声明我知道不应该被改变的东西变得很痛苦。`const` 是添加一点编译器强制健全性检查的好方法,它不应该这么难使用! (2认同)

Kev*_*Won 12

这只是一个猜测,但我认为原因可能与const值在编译时放入元数据(它具有所有它自己的微妙后果)这一事实有关.我想知道编译器是否有一些问题可以找出如何将var转换为元数据.

在Richter的CLR VIA C#中(第177页),

定义常量会导致元数据的创建.当代码引用常量符号时,编译器在定义该常量的程序集的元数据中查找该符号,提取常量的值,并将值嵌入到发出的IL代码中.

他接着指出,这意味着由于这个原因你无法获得对常量的记忆的引用.为了使它更加明确,在psuedo C#中,如果程序集A定义了一个const:

//Assembly A, Class Widget defines this:
public static const System.Decimal Pi = 3.14
Run Code Online (Sandbox Code Playgroud)

然后你有A的消费者:

//somewhere in the Program.exe assembly
decimal myCircleCurcum = 2 * Widget.pi
Run Code Online (Sandbox Code Playgroud)

由此得到的program.exe编译后的IL会做类似这样的伪代码:

// pseudo-IL just to illustrate what would happen to the const
myCircleCurcum = 2*3.14
Run Code Online (Sandbox Code Playgroud)

请注意,消费组件根本不知道小数3.14与程序集A有任何关系 - 它是program.exe的一个文字值.对我来说,这是C#编译器行为的合理方式 - 毕竟,程序集A 明确声明pi是一个常量(意味着值是一次且对于所有pi = 3.14).但是,我冒昧地猜测,99%的C#开发人员不理解这种情况的后果,可能会随心所欲地将pi改为3.1415.

常量具有非常差的跨组件版本故事(同样,这来自Richter),因为如果程序集A的常量发生变化(即它被重新编译),具有常量的程序集A的使用者将不会看到更改.这可能导致很难找出组件A的消费者的错误..所以我禁止我的团队使用常量.他们的轻微性能增加不值得他们可能导致的微妙错误.

你真的只能使用一个常数,如果你知道这个值永远不会改变 - 甚至把某些东西设置成一个const就像pi一样,你不能肯定地说你不希望你的percision改变在将来.

如果程序集A定义:

decimal const pi = 3.14
Run Code Online (Sandbox Code Playgroud)

然后你构建它,然后其他程序集使用它,如果你然后更改程序集A:

decimal const pi = 3.1415
Run Code Online (Sandbox Code Playgroud)

并重建程序集A,程序集A的使用者仍将具有旧值3.14!为什么?因为原始3.14被定义为常量,这意味着程序集A的消费者被告知该值不会改变 - 因此他们可以将pi的值烘焙到他们自己的元数据中(如果你重建了程序集A的消费者)然后将在其元数据中获得pi的新值.同样,我不认为这是CSC处理常量的方式的问题 - 只是开发人员可能不希望不能安全地更改常量在某些情况下,可以在其他情况下安全地更改.安全:没有消费者永远只能通过.dll引用(即它们将始终从源构建每个时间),不安全:消费者不知道你的程序集的源代码何时定义它改变了.在.NET文档中可能应该更加清楚,常量意味着您无法更改源代码中的值

出于这个原因,我强烈建议不要使用常量,而只是简单地使小部件.你能真正说出多少值真的永远是永恒的?

在我的脑海中使用const而不是readonly的唯一真正原因是,某些东西可能会对性能产生影响......但如果你遇到这种情况,我会想知道C#是否真的是你问题的正确语言.简而言之,对我而言,使用常量绝不是一个好主意.极少数情况下,微小的性能改善值得潜在的问题.


Kei*_*ght 11

我同意埃里克的观点,认为这是罪恶的丑陋:

const var s = "abcd"

但为什么不简单呢?

const s = "abcd"

对我来说似乎是一个合理的语法.

  • 同意.如果var可以推断类型,为什么不const? (6认同)

Mar*_*ers 7

简短的回答是因为语言设计师(微软)这么说.

来自MSDN:

编译器错误CS0822

错误消息:隐式键入的本地不能是const

隐式类型的局部变量仅用于存储匿名类型.在所有其他情况下,他们只是一个方便.如果变量的值永远不会改变,只需给它一个显式类型.尝试将readonly修饰符与隐式类型化的本地一起使用将生成CS0106.

纠正此错误

如果要求变量为常量或只读,请为其指定显式类型.