声明本地变量为const

14

显然,将局部变量声明为const可以防止运行时修改。我相信const实例变量是静态的。这是否对const局部变量的性质和用途有任何影响?(例如多线程)


10
如果您想要准确了解常量字段的定义,请阅读C#规范的第10.4节。这里的各种答案中似乎存在着一些误传和其他信息。 - Eric Lippert
7个回答

17

"const"变量必须具有原始类型(例如int,bool)。无论是本地还是全局的,每当“const”变量出现在源代码中时,该实例都将被替换为const值本身。因此:

const int foo = 42;
return foo + 69;

优化后变成:

return 42 + 69

或者说:

return 111;

由于const变量具有基本类型并且它们仅在编译时存在,因此不存在线程问题。


4
这个回答有一些错误。首先,“const 变量”是一个矛盾词;常量从来不是变量,因为变量是包含可以改变的值的“存储位置”,而常量是“值”(不是存储位置),它们不能改变。 - Eric Lippert
4
其次,常量不必是“原始”类型。常量可以是任何内置值类型(如int、short等),或任何字符串常量,或任何引用类型的null引用。 - Eric Lippert
11
第三,我不理解原始类型或编译时已知与线程有什么关系。常量不是线程安全的“因为它们是原始类型”。 常量之所以线程安全是因为根据定义它们不能改变,因此在两个不同的线程上永远不会观察到其具有不一致的值。 - Eric Lippert
@EricLippert 我也这样认为,但我想指出sthailk所说的“它们只存在于编译时”的意思是const名称在编译步骤中被优化和与“字面值”变量混合在一起。 - Gustavo6046

14

常量不是变量,也不会被实际地存储。由于它没有被存储,所以它既不是实例成员,也不是静态成员。

常量只是表示一个值的名称。当代码被编译时,该值会被插入到使用常量的位置。(如果您在另一个程序集中声明了一个常量,则更改常量的声明值不会更改使用该常量的值,直到重新编译使用常量的代码。)

因此,本地声明的常量的工作方式与在其他任何地方声明的常量完全相同,只是范围不同而已。


澄清一下 - 我看到 const 被称为 static 的地方是不正确的? - Ben Aston
@Ben:是的,常量不是静态的。静态变量具有静态存储,而常量根本没有存储。 - Guffa
@Ben,@Guffa。不,那不完全正确。仅因为常量没有相关的存储位置并不意味着它不是成员!常量字段是其声明类型的成员,实际上它是静态成员。 - Eric Lippert
1
@Eric:是的,你说得对,常量被归类为静态成员(出于某种原因),但它们并不是静态存储的,这是这个问题的重点。 - Guffa

13

const并不是一个变量,这就是为什么它被称为常数。


6
根据维基百科,“常数是一种特殊的变量”。http://en.wikipedia.org/wiki/Constant_(programming)。对于OQ中术语的懒惰使用,我表示抱歉。 - Ben Aston
const在其原始含义上不是变量(即它不是可变的,而是常量)。然而,我非常确定“const int”可以被称为const变量。 - Janick Bernet
20
@Ben: 就 C# 语言而言,维基百科是错误的。在 C# 中,常量不是任何类型的变量。C# 中,变量被定义为存储位置,其内容可以变化。常量既不是存储位置,也不能变化。 - Eric Lippert

4
我希望能插话一下,我觉得已经给出的共识答案并不完整。 总结这些答案,共识是我们应该考虑以下代码不是变量声明,而是一种宏声明,在编译器内联const值,无论标识符在哪里使用:
const int foo = 42;

然而,如果使用(可能复杂的)常量表达式声明const“变量”,那么就会引发一些问题,如下所示:
const double H = 1.23e-2, Q = 7.65e-4, nu = 0.3;
const double Reynolds = H*H*H*H / Q / (1d - nu);

在这种情况下,编译器是否一次计算表达式并“存储”结果以供重复使用(类似于静态变量),或者每次使用标识符时都执行表达式(类似于C/C ++中的#define宏)将产生影响。
在我自己的探索中,我在http://www.techopedia.com/definition/3768/constant-c中找到了以下描述,涉及此问题:
在C#上下文中,常量是一种类型的字段或局部变量,其值在编译时设置,永远不能在运行时更改。它类似于具有名称、值和内存位置的变量。然而,它与变量不同之处在于,在应用程序中只初始化一次。使用关键字“const”声明常量。
可以承认,“内存位置”部分有点牵强 - 我认为这意味着在编译期间本地存储const值。正如在其他地方提到的那样,您无法在代码中访问或引用此内存。否则,这似乎与我在C#语言规范中读到的一致。

8.5.2 本地常量声明

local-constant-declaration 声明一个或多个本地常量。

local-constant-declaration:

const type {constant-declarators ,} constant-declarator

constant-declarator:

identifier = constant-expression

并且:

7.19 常量表达式

constant-expression 是可以在编译时完全计算的表达式。

constant-expression:

expression

...

只要表达式满足上述要求,即使表达式是包含非常量结构的较大表达式的子表达式,该表达式也会在编译时计算。

欢迎对此“quanser”提供任何反馈:^)


有趣的是,C#编译器实际上允许在编译时评估常量的操作,因此即使const像C++宏一样执行并在代码中丢弃整个未计算的表达式,每个单独的表达式都将被计算为一个单一的常量值在编译时。 在将值放置在其使用的任何地方之前进行一次计算将加快编译时间(我确信它会这样做),但它不会对程序的执行速度产生任何影响。 - Servy
当然可以访问它。你读取变量的值。 - John Lord

3

由于每次方法调用都会创建自己的堆栈区域,因此拥有自己的局部变量,您不必担心其他线程修改局部变量。

据我所知,在C#中将局部变量创建为const不会创建任何变量或字段,而是在您在方法内部使用它的任何地方都会内联放置分配的常量值。


2

本地使用const的主要优点是,您不会意外地将标识符设置为另一个值,这可能会改变代码的正确性。


-3
我也在为什么不能在C#中使用var声明常量?发布了这篇文章。
没有var的常量:
const int Value1 = 1;
const int Value2 = 2;

使用 var 声明的常量(匿名类型属性值在创建后无法更改):

var constants = new { 
  Value1 = 1, 
  Value2 = 2,
};
//use as constants.Value1

4
您所创建的对象是不可变的,但您放置它的变量可以被修改以指向完全不同的另一个对象,这使得它与常量字段完全不同。 - Servy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接