在C#中推断常量

在C#中,以下types推断的工作原理是:

var s = "abcd"; 

但是,为什么当variables是一个常量时不能推断出这个types呢?

下面抛出一个编译时exception:

 const var s = "abcd"; // <= Compile time error: // Implicitly-typed local variables cannot be constant 

我其实希望Lippertstream行,并看看这个问题

如果有什么你想要引起我的注意的,你可以把我的名字留在文本中 – 而不是一个评论 – 我会最终find它。 或者,更好的是,你可以“鸣叫” @ericlippert 。 请注意,这不构成服务级别协议; 我在业余时间做这个。

为什么当variables是一个常量时不能推断出这个types呢?

“常量”和“variables”是相反的const var给了我颤抖的types。 常数是一个永不改变,没有存储位置的值; variables是内容发生变化的存储位置。 他们完全不同,所以不要试图把它们结合起来。 var语法被select来调出“这是一个variables”,我们坚持下去。

var可以代表一个特定的types声明,但是将它和const结合起来,严重影响了编译器这个值所做的事情。 因此const var是不允许的,以防止这种混淆,你必须明确地键入你的常量。

推断的常量不会使用var

 const Pi = 3.14159; 

对我来说似乎很好。 不过,我知道没有计划将此添加到C#。

这只是一个猜测,但我认为这个原因可能与编译时将const值放入元数据(这个元数据本身具有微妙的后果)这一事实有关。 我想知道如果编译器有一些问题搞清楚如何将变换到元数据。

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

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

他继续指出,这意味着你不能得到一个常数的记忆引用这个原因。 为了更清楚一点,在psuedo C#中,如果程序集A定义了一个const:

 //Assembly A, Class Widget defines this: public static const System.Decimal Pi = 3.14 

那么你有一个消费者A:

 //somewhere in the Program.exe assembly decimal myCircleCurcum = 2 * Widget.pi 

program.exe的结果编译的IL会做这样的伪代码:

 // pseudo-IL just to illustrate what would happen to the const myCircleCurcum = 2*3.14 

请注意, 使用程序集并不知道小数3.14与程序集A有任何关系 – 它是program.exe的一个字面值 。 对我来说,这对于C#编译器来说是一个合理的方法。毕竟,Assembly A 明确声明了pi是一个常量(意思是该值是一次性的,而且是pi = 3.14)。 但是,我想冒险猜测,99%的C#开发人员不理解这个分支,可能会改变pi 3.155。

常量有一个非常差的交叉汇编版本的故事(同样来自Richter),因为如果程序集A的常量发生变化(即重新编译),那么带有常量的程序集A的消费者将不会看到变化。 这可能会导致真正难以找出程序集A的消费者的错误。 。 以至于我禁止我的团队使用常量。 他们轻微的性能提升并不值得他们所能造成的微妙的缺陷。

如果你知道这个值永远不会改变,那么你永远只能使用一个常数 ,即使像pi这样的常量被设置为一个常量,你也不能肯定地说你不希望你的偏好改变在将来。

如果程序集A定义:

 decimal const pi = 3.14 

然后你build立它,然后其他程序集消耗它,如果你然后更改程序集A:

 decimal const pi = 3.1415 

并重新组装A,组件A的用户仍旧具有旧值3.14! 为什么? 因为原来的3.14被定义为一个常数,这意味着大会A的消费者已经被告知价值不会改变 – 所以他们可以将pi的价值烘焙到他们自己的元数据中(如果你重新组装A的消费者,然后将在它的元数据中获得pi的新值)。 再次,我不认为这是CSC处理常量的一个问题 – 只是开发人员可能不希望在某些情况下不能安全地更改常量,而在其他情况下可以安全地更改常量。 安全:没有消费者永远只能通过.dll引用(即他们将始终从源码开始构build),不安全:消费者不知道何时使用const定义了它的源代码。 在.NET文档中可能应该更清楚地表明你不能改变源代码中的值

出于这个原因,我强烈build议不要使用常量,而只是使该构件只读。 你真的可以肯定地说,有多less价值观永远是永恒的?

在我脑海中只使用const的唯一真正的原因是,如果有什么东西可能会影响性能,但是如果你遇到了这个问题,我不知道C#是否真的是你的问题的正确语言。 总之,对我来说,使用常量从来就不是一个好主意。 有很less的时候,这个小小的改进值得潜在的问题。

我同意埃里克认为这是丑陋的罪过:

const var s = "abcd"

但为什么不简单呢?

const s = "abcd"

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

简短的答案是因为语言devise师(微软)这样说。

来自MSDN :

编译器错误CS0822

错误消息 :隐式types的本地人不能是常量

隐式types化的局部variables只对存储匿名types是必需的。 在所有其他情况下,它们只是一种方便。 如果variables的值永远不会改变,就给它一个明确的types。 尝试使用readonly修饰符与隐式types的本地将生成CS0106。

纠正这个错误

如果你需要variables是常量或只读,给它一个明确的types。

我不同意@Eric。

var关键字并不意味着“这是一个variables”,这意味着“该types是推断”。

int,long等是否是“关键字”来识别variables? 不,它们只是数据types,可以用于variables或常量。

我认为关键字var的名字被认为是类似Javascript,我认为不合适。

汽车怎么样? ( jtc1/sc22/wg21/docs/papers/2004/n1705.pdf

我的答案? 由于目前不可能使用“const var”,所以不用担心。 这个限制无缘无故,使得C#在处理常量和variables方面存在不平衡,并且造成了不对称。 你会过得更好

“”var“语法被select为”这是一个variables“,我们坚持这一点。

我发现埃里克·利波特(Eric Lippert)的论点在多个层面上都是无法令人信服的。

埃里克,我不知道谁是“我们”,我真的不想听起来粗鲁无礼,但无论(因为存在的原因)和意义 (如为什么var是适当的名称)无关你试图附加到它的意思。 “Var”被用作显式types声明的地方,并表示它是types的事实,在那个时候可以是许多types中的一个。

回顾一下,varreplace了types声明。 我们不要假装它做任何事情,因为types和值(以及这个值是否可以改变)是两个截然不同的东西。 Occum的剃刀适用于这里,没有必要扩大var的含义。

更重要的是,即使在隐式声明不是一个选项,var关键字被使用的时候,人们仍然认为它们的对象是variables,并且没有问题将它们的variables声明为常量。

“var”是因为需要而引入的。 而且这个需求不是让variables变成常量。 这种有限的交错带来了另一个需要,那就是目前不符合。

你的整个立场可以推断为一个symantics的论点 – 我们根本不喜欢“const var sounds”的方式(例如“给我打了个颤抖”)这很奇怪,考虑到可以input类似“dynamic static”没有编译错误,这听起来尴尬。

为什么强调一些绝对没有冒险的东西呢? 是“常量variables=”你好世界“”或其一些变化真的会让人困惑的天气这是一个常数或不。 我认为人们将能够明确地理解这意味着什么,就像他们理解“dynamic静态”的含义一样。

真正的底线是能够隐式地声明常量都是有意义的,并且实际上可以是有用的。 目前没有办法做到这一点似乎没有理由。 而且,能够声明“const var”比引入又一个关键字来隐式声明的常量更有意义。

如果你不认为Eric的论点完全是基于对语义的不必要的复杂解释,那么如果用不同的名字来调用,就围绕“var”的含义build立相同的论点。 说,impl。 impl不能和const一起使用会有什么原因吗? 我很难想出一个单一的原因。 因此,归结为不喜欢“const var”的声音而没有别的。 我认为我们大多数人可以很容易地克服这一点。

在这种情况下,显然你知道引用types是常量,而且是一个相当原始的types(consts只能是值types或者string等),所以你应该声明这个types,而不是使用隐式types。

换句话说,因为这个types明显是常量和已知的,所以绝对没有理由使用var。

隐式types化的局部variables只对存储匿名types是必需的。 在所有其他情况下,它们只是一种方便。 如果variables的值永远不会改变,就给它一个明确的types。 尝试使用readonly修饰符与隐式types的本地将生成CS0106。

http://msdn.microsoft.com/en-us/library/bb310881.aspx

编译器错误CS0822

纠正这个错误如果你需要variables是常量或只读的,给它一个明确的types。

虽然我不同意Lippert先生的推理,但是有一个很好的理由不允许对命名常量进行隐式types化:如果types化的常量不需要显式地指定它们的types,请考虑以下代码的含义:

 const var ScaleFactor = 2500000000; // Type 'Int64' ... int thisValue = getNextInt(); total += thisValue * ScaleFactor; 

现在假设比例因子需要降低20%。 将价值改为20亿的效果会是怎样? 尽pipeInt64成为Int32的问题将会发生,即使在代码中指定了值[例如,当改变total += thisValue * 2500000000; total += thisValue * 2000000000; 该更改将与要求该值为Int64的代码相邻。 相比之下, const声明可能会远离代码的影响,所以没有可视的方式来知道某个地方的代码是否可以依赖于一个常量types。

有趣。 我不知道它是否仅仅是C#编译器的一个限制,或者它是否是对语言本身的一个限制。

为了解释我的意思,考虑VB。

在VB 9中,你也不能推断常量,但这只是编译器的限制。 在VB 10中,他们能够添加常量types推断,而不会对语言做任何重大的改变。

IMO var的主要目的是允许匿名types(types是未知的,用var你可以声明一个variables来存储它)。 现在更常见的用法是编写更less的代码;)。 正如他们在这里解释的,如果你知道types和值(不会改变),只要写下types即可。

我意识到我(我们)实际需要的是在JavaScript或C中定义的const关键字的行为。也就是说,能够在运行时计算它,但不允许在后续代码中更新它。 这对于强制规定是有用的,并且当你只需要一次计算一个值时就是明确的。

换句话说,这个问题真正要求的是能够在本地使用readonly关键字(variables/方法参数)。 也就是说,这个语法可能是有用的:

 // This variable should never be overwritten! readonly var target = 4; 

这不是在C#中没有先例。 using()和迭代( foreach )variables已经有这样的行为:

 class Program { static void Main(string[] args) { foreach (var x in new[] { "asdf", }) { System.Console.WriteLine(x); // error CS1656: Cannot assign to 'x' because it is a 'foreach iteration variable' x = "food"; } } } 

哦,看,我得到了types推断 readonly行为! 好极了! 然而,使用foreach关键字太笨重,不能在真正的代码中实现。 一点都不明显,你试图保护你自己,呃,你自己或你的同事join代码,稍后改变x而不考虑其含义(在编译器错误的帮助下)。 这就是为什么如果它成为一种语言function将是伟大的 。