Type-inferring a constant in C#

C#ConstantsType Inference

C# Problem Overview


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

C# Solutions


Solution 1 - C#

> 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 - C#

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 - C#

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 - C#

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 - C#

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.

Solution 6 - C#

I don't agree with @Eric.

The var keyword doesn't mean "this is a variable", it means "the type is to be inferred".

Did int, long, etc. are "keywords" to identify variables? No, they are just data types, can be used for variables or constants.

I think the name of the keyword var was thought to resemble Javascript and I consider inappropriate.

How about auto ? ( http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1705.pdf )

Solution 7 - C#

While I disagree with Mr. Lippert's reasoning, there is a good reason not to allow implicit typing of named constants: consider the meaning of the following code, if typed constants did not have to specify their type explicitly:

const var ScaleFactor = 2500000000; // Type 'Int64'

...
int thisValue = getNextInt();
total += thisValue * ScaleFactor;

Now suppose that the scale factor needs to be notched down by 20%. What would be the effect of changing the value to 2000000000? While the problem of having an Int64 become an Int32 would occur even if the value were specifed in the code [e.g. when changing total += thisValue * 2500000000; to total += thisValue * 2000000000; the change would be adjacent to the code that requires that the value be an Int64. By contrast, a const declaration would likely be far removed from the code it effects, so there would be no visible way of knowing whether code somewhere might rely upon a constant being a long type.

Solution 8 - C#

In this case it is obvious that you know the reference type will be constant, and of a fairly primitive type (consts can only be value types, or strings, etc..), so you should declare that type, rather than use implicit typing.

In other words, because the type is obviously constant and known, there's absolutely no reason to use var.

> 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.

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

Compiler Error CS0822

> To correct this error If you require > the variable to be constant or > readonly, give it an explicit type.

Solution 9 - C#

I realized that what I (we) actually wanted is the behavior of the const keyword as defined in JavaScript or C. That is, the ability to calculate it at runtime but disallow updating it in subsequent code. This can be useful to force discipline and be explicit when you only want a value to be calculated once.

In other words, what this question is really asking for is to be able to use the readonly keyword on locals (variables/method parameters). I.e., this syntax might be useful:

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

It is not like there is no precedent for this in C#. using() and iteration (foreach) variables already behave this way:

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";
        }
    }
}

Oh, look—I got type inference and readonly behavior! Yay! However, using the foreach keyword is way too clunky to actually do this in real code. It is not obvious at all that you’re trying to protect yourself from, uh, yourself or your coworkers adding code that mutates x later without thinking through the implications (with the help of a compiler error). That is why it would be great if it became a language feature.

Solution 10 - C#

Interesting. I don't know if it is just a limitation of the C# compiler or if it a fundemental limitaion of the language itself.

To explain what I mean, consider VB.

In VB 9 you also couldn't infer constants, but this was just a limitation of the compiler. In VB 10 they were able to add constant type inference without making any significant changes the to language.

Solution 11 - C#

IMO var main purpose is to allow anonymous types (type is unknown, with var you can declare a variable to store it). The more common usage now is to write less code ;). As they explain here if you know the type and the value (which won't change) just write the type.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAndreas GrechView Question on Stackoverflow
Solution 1 - C#Eric LippertView Answer on Stackoverflow
Solution 2 - C#Keith Sparkjoy - PluralsightView Answer on Stackoverflow
Solution 3 - C#Kevin WonView Answer on Stackoverflow
Solution 4 - C#Mark ByersView Answer on Stackoverflow
Solution 5 - C#user1179071View Answer on Stackoverflow
Solution 6 - C#Fernando PelliccioniView Answer on Stackoverflow
Solution 7 - C#supercatView Answer on Stackoverflow
Solution 8 - C#BobbyView Answer on Stackoverflow
Solution 9 - C#binkiView Answer on Stackoverflow
Solution 10 - C#Jonathan AllenView Answer on Stackoverflow
Solution 11 - C#BekuView Answer on Stackoverflow