Type-inferring a constant in C#
In C#, the following type-inference works:
var s = "abcd";
But why can't the type be inferred when the variable is a constant?
The following throws a compile-time exception:
const var s = "abcd"; // <= Compile time error:
// Implicitly-typed local variables cannot be constant
Solution 1:
I'm actually hoping Lippert pops by and and takes a look at the question
If there's something you want brought to my attention, you can leave my name in the text -- not a comment -- and I'll find it eventually. Or, better, you can "tweet" to @ericlippert
. Note that this does not constitute a service level agreement; I do this in my spare time.
why can't the type be inferred when the variable is a constant?
"constant" and "variable" are opposites. const var
gives me the shudders to type. A constant is a value that never changes and has no storage location; a variable is a storage location whose contents change. They're completely different, so don't attempt to combine them. The var
syntax was chosen to call out "this is a variable", and we're sticking with it.
var
can stand in for a specific type declaration, but combining it with const
severely muddies the picture of what the compiler does with the value. Therefore const var
is disallowed to prevent this confusion and you have to explicitly type your constants.
I would be perfectly fine with inferred constants that do not use var
:
const Pi = 3.14159;
seems fine to me. However, I know of no plans to add this to C#.
Solution 2:
I agree with Eric that this is ugly as sin:
const var s = "abcd"
But why not simply this?
const s = "abcd"
Seems like a reasonable syntax to me.
Solution 3:
This is just a guess, but I think that the reason might have to do with the fact that const values are put in metadata (which has subtle consequences all it's own) when compiled. I wonder if maybe the compiler has some issues figuring out how to transform a var to metadata.
In Richter's CLR VIA C# (page 177),
Defining a constant causes creation of metadata. When code refers to a constant symbol, compilers look up that symbol in the metadata of the assembly that defines that constant, extract the constant's value, and embed the value in the emitted IL code.
He goes on to note that this means that you can't get the reference to memory of a constant for this reason. To make this a bit more explicit, in psuedo C# if assembly A defines a const:
//Assembly A, Class Widget defines this:
public static const System.Decimal Pi = 3.14
then you have a consumer of A:
//somewhere in the Program.exe assembly
decimal myCircleCurcum = 2 * Widget.pi
the resultant compiled IL of program.exe would do something like this pseudocode:
// pseudo-IL just to illustrate what would happen to the const
myCircleCurcum = 2*3.14
note that the consuming assembly has no idea that the decimal 3.14 had any relationship to Assembly A at all--it is to program.exe a literal value. This, to me, is a reasonable way for the C# compiler to act--after all, Assembly A declared explicitly that pi is a constant (meaning that the value is once and for all pi=3.14). But, I'd venture to guess, that 99% of C# developers do not understand the ramifications of this & might change pi to be 3.1415 on a whim.
Constants have a really poor cross-assembly version story (again, this comes from Richter) because a consumer of assembly A with a constant in it will not see a change if assembly A's constant changes (i.e. it was recompiled). This can cause really hard to figure out bugs by consumer of assembly A. . . so much so that I ban my team from using constants. Their slight perf gain is not worth the subtle bugs they can cause.
You can really only ever use a constant if you know that the value will never, ever change -- and even with something set as a const such as pi, you can't say for sure that you won't want your percision to change in the future.
if assembly A defines:
decimal const pi = 3.14
then you build it and then other assemblies consume it, if you then change assembly A:
decimal const pi = 3.1415
and rebuild assembly A, the consumer of assembly A will still have the old value 3.14! why? because the original 3.14 was defined as a constant which means that the consumers of assembly A have been told that the value won't change--so they can bake that value of pi into their own metadata (if you rebuild consumer of assembly A it will then get the new value of pi in it's metadata). Again, I don't see this as a problem with the way CSC handles constants--it's just that developers probably don't expect that a constant can't be changed safely under some circumstances, where it can be changed safely in others. Safe: no consumers will ever have reference by .dll only (i.e. they will always build from source EVERY TIME), unsafe: consumers don't have a clue about when source code of your assembly with the const defined it it changes. It probably should be made much more clear in .NET documentation that constant means you can't change the value in the sourcecode
For that reason, I'd strongly suggest not using constants and instead just making the widget readonly. How many values can you really say for certain are truly going to be const for ever and always?
The only real reason to use const over readonly in my mind is if something might have performance implications... but if you are running into that, I'd wonder if C# is really the correct language for your problem. In short, to me, it is alomst never a good idea to use constants. There are very few times where the tiny perf improvement is worth the potential problems.
Solution 4:
The short answer is because the language designers (Microsoft) say so.
From MSDN:
Compiler Error CS0822
Error Message: Implicitly typed locals cannot be const
Implicitly typed local variables are only necessary for storing anonymous types. In all other cases they are just a convenience. If the value of the variable never changes, just give it an explicit type. Attempting to use the readonly modifier with an implicitly typed local will generate CS0106.
To correct this error
If you require the variable to be constant or readonly, give it an explicit type.
Solution 5:
My answer? Since it is not currently possible to use "const var" don't even worry about it. That limitation, for no reason at all, makes C# unbalanced in how it treats constants versus variables and that creates an assymetry. You'd be better off
"The "var" syntax was chosen to call out "this is a variable", and we're sticking with it."
I find Eric Lippert's arguemnt deeply unconvincing on multiple levels.
Eric, I don't know who "we" are and I really don't want to sound rude but both the use (as in the reason for being) AND meaning (as in why var is appropriate name) have nothing to do with the meaning you are trying to attach to it. "Var" is used it place of an an explicit type declaration and signifies the fact that it's type, at that point in time, can be one of many.
To recap, var replaces the type declaration. Let's not pretend that it does anything else because type and value (and whether or not this value can be changed) are two distinct things. Occum's razor applies here and there is no need to expand the meaning of var beyond what it does.
More importantly, even in the days when implicit declarations were not an option and the var keyword was in use people still thought of their objects as variables and had no problem declaring their variables as constants.
"var" was introduced because there was a need for it. And that need was not to make variables safe from becoming constants. That limited interpritation creates another need, that is currently not meet.
Your whole stance can be deduced to a symantics argument - we simply don't like the way "const var sounds" (e.g. "gives me the shudders to type.") This is odd considering that one can type something like "dynamic static" without compilation errors and that sounds awkward too.
Sp why emphasize something that has absolutely no risk of being ambigious in the first place? Is "const var = "Hello World"" or some variation of thereof really going to make people puzzled weather it's a constant or not. I think people will be able to understand exactly what that means, just as they understand what "dynamic static" means.
The real bottom line is that being able to implicitly declare constants both makes sense and can actually be useful. There is currently no way to do that for seemingly no reason. And it makes a heck of a lot more sense to be able to declare "const var" than to introduce yet another keyword to serve implicitly declared constants.
And if you don't think that Eric's argument is entirely based on needlessly complex interpretation of semantics, try to build the same argument around the meaning of "var" if it's called by a different name. Say, impl. Would there be any reason why impl couldn't be used in conjunction with const? I'd be hard pressed to come up with a single reason for it. Therefore, it comes down to not liking the way "const var" sounds and nothing else. I think most of us could easily get over that.