在C#中,以下类型推断有效:
var s = "abcd";
但是当变量是常数时,为什么不能推断出类型?
以下命令抛出编译时异常:
const var s = "abcd"; // <= Compile time error:
// Implicitly-typed local variables cannot be constant
答案 0 :(得分:31)
我实际上希望Lippert会出现并且看看问题
如果你想引起我的注意,你可以把我的名字留在文本中 - 而不是评论 - 我最终会找到它。或者,更好的是,您可以“鸣叫”到@ericlippert
。请注意,这不构成服务级别协议;我在业余时间这样做。
为什么在变量是常量时不能推断出类型?
“常数”和“变量”是对立。 const var
让我感到不寒而栗。常量是一个永远不会改变且没有存储位置的值;变量是其内容发生变化的存储位置。它们完全不同,所以不要试图将它们结合起来。选择var
语法来调出“这是一个变量”,我们坚持使用它。
var
可以代表一个特定的类型声明,但是将它与const
严重混淆了编译器所做的的图片。因此,不允许使用const var
来防止这种混淆,您必须明确键入常量。
对于不使用var
的推断常量,我会完全没问题:
const Pi = 3.14159;
对我来说似乎很好。但是,我知道没有计划将其添加到C#。
答案 1 :(得分:12)
这只是一个猜测,但我认为原因可能与const值在编译时放入元数据(它具有微妙的后果)这一事实有关。我想知道编译器是否有一些问题可以找出如何将var转换为元数据。
在Richter的 CLR VIA C#(第177页)中,
定义常量会导致创建 元数据。当代码引用a时 常数符号,编译器查找 该元数据中的符号 定义该常量的程序集, 提取常量的值,然后 将值嵌入发出的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#编译器来说,这是一种合理的方式 - 毕竟,程序集A 明确声明,pi是一个常量(意味着值是一次且对于所有pi = 3.14) 。但是,我冒昧地猜测,99%的C#开发人员并不了解这个&amp; amp;的结果。可能会随心所欲地将pi改为3.1415。
常量具有非常差的跨组件版本故事(同样,这来自Richter),因为如果程序集A的常量发生变化(即重新编译),组件A中具有常量的消费者将不会看到更改。这可能导致很难找出组件A的消费者的错误。 。所以我禁止我的团队使用常量。他们的轻微性能增加不值得他们可能导致的微妙错误。
你真的只能使用一个常数,如果你知道这个值永远不会改变 - 甚至设置为像pi这样的const,你也不能肯定地说你不会希望你的percision在将来改变。
如果程序集A定义:
decimal const pi = 3.14
然后你构建它,然后其他程序集使用它,如果你然后更改程序集A:
decimal const pi = 3.1415
并重建程序集A,程序集A的使用者仍将具有旧值3.14!为什么?因为原始3.14被定义为常量,这意味着程序集A的消费者被告知该值不会改变 - 因此他们可以将pi的值烘焙到他们自己的元数据中(如果你重建了程序集A的消费者)然后将在其元数据中获得pi的新值。同样,我不认为这是CSC处理常量的方式的一个问题 - 只是开发人员可能不希望在某些情况下不能安全地改变 在其他人可以安全地改变。安全:任何消费者都不会仅通过.dll引用(即它们将始终从源构建每个时间),不安全:消费者对于使用const定义它的程序集的源代码何时更改时没有任何线索。在.NET文档中可能应该更加清楚常量意味着您无法更改源代码中的值
出于这个原因,我强烈建议不要使用常量,而只是简单地使小部件。你能真正说出多少值真正永远是永恒的?
在我的脑海中使用const而不是readonly的唯一真正原因是,某些东西可能会对性能产生影响......但如果你遇到这种情况,我会怀疑C#是否真的是你问题的正确语言。简而言之,对我而言,使用常量绝不是一个好主意。极少数情况下,微小的性能改善值得潜在的问题。
答案 2 :(得分:11)
我同意埃里克的观点,认为这很丑陋:
const var s = "abcd"
但为什么不简单呢?
const s = "abcd"
对我来说似乎是一种合理的语法。
答案 3 :(得分:7)
简短的回答是因为语言设计师(微软)这么说。
来自MSDN:
编译器错误CS0822
错误消息:隐式输入的本地人 不能是const
隐式输入的局部变量是 只需要存储匿名 类型。在所有其他情况下,他们是 只是方便。如果值 变量永远不会改变,只需给出 它是一个明确的类型。试图使用 readonly修饰符 隐式输入本地将生成 CS0106。
更正此错误
如果您要求变量保持不变或只读,请给它一个 显式类型。
答案 4 :(得分:3)
我不同意@Eric。
var 关键字并不意味着“这是一个变量”,它意味着“要推断出类型”。
int,long等是否是识别变量的“关键字”?不,它们只是数据类型,可以用于变量或常量。
我认为关键字 var 的名称被认为类似于Javascript,我觉得不合适。
自动怎么样? (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1705.pdf)
答案 5 :(得分:3)
我的回答?由于目前无法使用“const var”,所以甚至不用担心它。这种限制,完全没有任何理由,使得C#在处理常量与变量的方式上不平衡,从而产生了不对称性。你会好起来的
“选择”var“语法来调用”这是一个变量“,我们坚持使用它。”
我发现Eric Lippert的争论在多个层面上都非常难以理解。
Eric,我不知道“我们”是谁,我真的不想听起来粗鲁但是使用(因为存在的原因)和意思< / strong>(因为var为什么是合适的名称)与你试图附加到它的含义无关。 “Var”是使用它是一个显式类型声明的位置,表示它的类型,在那个时间点,可以是众多之一。总结一下,var取代了类型声明。让我们不假装它做任何其他事情,因为类型和值(以及这个值是否可以改变)是两个截然不同的事情。 Occum的剃刀适用于此,并且不需要扩展var的含义超出它的含义。
更重要的是,即使在隐式声明不是一个选项并且var关键字正在使用的日子里,人们仍然认为它们的对象是变量,并且没有问题将它们的变量声明为常量。
引入“var”因为需要它。而这种需求并不是为了使变量不会成为常量。有限的interpritation产生了另一种需求,目前尚未满足。
你的整个立场可以推断出一个语法论点 - 我们根本不喜欢“const var sounds”的方式(例如“给我颤抖的类型。”)这很奇怪,因为人们可以输入“动态静态“没有编译错误,听起来也很尴尬。
为什么要强调一开始就没有任何风险的东西?是“const var =”Hello World“”还是其中的一些变化真的会让人们感到困惑,天气是不变的。我认为人们将能够准确理解这意味着什么,就像他们理解“动态静态”的含义一样。
真正的底线是能够隐式声明常量既有意义又可以实际有用。目前似乎没有理由,目前无法做到这一点。能够声明“const var”比引入另一个关键字来提供隐式声明的常量更有意义。
如果你不认为Eric的论证完全是基于对语义的不必要的复杂解释,那么如果用不同的名字调用,那么试着围绕“var”的含义构建相同的参数。 。说,impl。是否有任何理由为什么impl不能与const一起使用?我很难想出一个理由。因此,它归结为不喜欢“const var”声音的方式,而不是别的。我想我们大多数人都可以轻易克服这一点。
答案 6 :(得分:2)
在这种情况下,很明显你知道引用类型是常量的,并且是一个相当原始的类型(consts只能是值类型,或字符串等等),所以你应该声明那个类型,而不是使用隐式输入。
换句话说,因为类型显然是常数且已知,所以绝对没有理由使用var。
隐式输入的局部变量是 只需要存储匿名 类型。在所有其他情况下,他们是 只是方便。如果值 变量永远不会改变,只需给出 它是一个明确的类型。试图使用 readonly修饰符 隐式输入本地将生成 CS0106。
http://msdn.microsoft.com/en-us/library/bb310881.aspx
编译器错误CS0822
如果需要,请更正此错误 变量是常数或 readonly,给它一个明确的类型。
答案 7 :(得分:2)
虽然我不同意Lippert先生的推理,但有充分理由不允许隐式输入命名常量:考虑以下代码的含义,如果类型常量不必明确指定其类型:
const var ScaleFactor = 2500000000; // Type 'Int64'
...
int thisValue = getNextInt();
total += thisValue * ScaleFactor;
现在假设比例因子需要减少20%。将值更改为2000000000会产生什么影响?虽然在代码中指定了值,但是Int64成为Int32的问题会发生[例如,将total += thisValue * 2500000000;
更改为total += thisValue * 2000000000;
时,更改将与要求值为Int64
的代码相邻。相比之下,const
声明可能会远离它所影响的代码,因此没有可见的方法来了解某处的代码是否可能依赖于常量为long类型。
答案 8 :(得分:1)
有趣。我不知道它是否只是C#编译器的限制,或者它是语言本身的有趣限制。
要解释我的意思,请考虑VB。
在VB 9中,你也无法推断常量,但这只是编译器的限制。 在VB 10中,他们能够添加常量类型推断,而无需对语言进行任何重大更改。
答案 9 :(得分:0)
IMO var的主要目的是允许匿名类型(类型未知,使用var可以声明一个变量来存储它)。现在更常见的用法是编写更少的代码;)。 如果您知道类型和值(不会改变),他们会解释here,只需写出类型。
答案 10 :(得分:0)
我意识到我(我们)实际想要的是JavaScript或C中定义的const
关键字的行为。也就是说,它能够在运行时计算它,但不允许在后续代码中更新它。当你只想要计算一次值时,这对于强制纪律和明确是有用的。
换句话说,这个问题的真正要求是能够在本地使用readonly
关键字(变量/方法参数)。即,这种语法可能很有用:
// This variable should never be overwritten!
readonly var target = 4;
这并不像C#中没有先例。 using()
和迭代(foreach
)变量已经以这种方式运行:
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";
}
}
}
哦,看 - 我得到了类型推断和 readonly
行为!好极了!但是,使用foreach
关键字实际上在实际代码中实际执行此操作过于笨拙。根本没有明显的是,你试图保护自己,呃,你自己或你的同事添加的代码在不考虑影响的情况下(在编译器错误的帮助下)改变x
。这就是it would be great if it became a language feature。