Type-inferring a constant in C#
Asked Answered
P

11

56

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
Poyssick answered 24/1, 2010 at 19:33 Comment(6)
My hunch is that you are abusing the var. If you know the type, make it readonly string or const string.Blythe
Actually, it's most about curiosity.Poyssick
Agreed. I believe the var keyword was more intented to ease up on the whole Dictionary<string, int> myDictionary = new Dictionary<string, int>() deal. Although Andreas does pose an interesting question and I can't think of any technical reason why const var s = "abcd" would not be valid. This would be a perfect question for Eric Lippert (blogs.msdn.com/ericlippert). If you haven't read his blog and you're interested in this sort of stuff it's a can't miss.Kristiekristien
@Brian: I think that the main purpose of the var keyword was to allow for anonymous types to be used; this is the one and only case where you really need the var keyword.Deanndeanna
@Fredrik: Yeah, sorry about that. I can see where I may have misled with my comment. Obviously the var keyword isn't necessary in the scenario I described but it is useful there. As you stated it's required for anonymous types.Kristiekristien
@Brian: I searched a bit for it and found a post on his blog that mentions both of our suggested reasons as driving forces to introduce the var keyword: blogs.msdn.com/ericlippert/archive/2005/09/27/…Deanndeanna
A
45

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

Arvind answered 24/1, 2010 at 20:11 Comment(13)
But is there a specific reason on why the designers chose not to implement type-inference for constants yet allow the use of var as syntactic sugar for known types (like var s = "abc";) ?Poyssick
@Andreas, There are two main uses for impl typed locals: anonymous types, and elimination of redundancy. It is a pain to type Dictionary<string, List<decimal>> priceTable = new Dictionary<string, List<decimal>>(); -- the "var" eliminates the completely unnecessary verbose type declaration. Your example is an example of the latter, though I personally would frown upon var s = "abc"; or var i = 123; or, worst of all, var m = M();Arvind
If I have an equation in math and it contains a "variable" this variable often really is a constant. For example in "2x = 6" x is and always will be the value 3. Some people get shuddered by something like "x = x + 1", which is an insolvable equation, yet common in many programming languages including C#.Whomp
@helium, the problem there is that programming languages abuse the = operator to mean "assign this value to this storage location", instead of using it to mean either "evaluate the truth or falsity of this equality", or "declare the equivalence of these two entities". I personally would rather that the = operator have been something like <-- in C/C++/C#/etc, but for historical reasons, we're stuck with = to mean assignment.Arvind
@Eric: I wonder if this is also related to your post "Why no var on fields?" - const is not a variable, but I wonder if the compiler handles const and fields similar? blogs.msdn.com/ericlippert/archive/2009/01/26/…Ia
@Michael: the situations are not exactly the same but they are similar. I noted that we would have to worry about cyclic definitions in analysis of "var" fields. Today we have to worry about cycles in analysis of constants. If you say const int x = y + 10;, then the definition of const int y had better not depend on x.Arvind
as someone else mentioned, const could behave the same as var in that it implicitly gets the type (i.e. const s = "whatever" rather than const var s), but this answer reasonably explains why that could still be a bad idea.Missilery
FSharp has no problem with type inference on let x = 10.0 Type inference on the rhs expression seems an orthogonal concept to the mutability of the reference. var for mutable reference. const for immutable reference. Type inference for both makes sense.Jermaine
As well from the C# doc with respects to the var keyword. This keyword specifies that the value of the field or the local variable is constant, which means that it can't be modified. Note the inference that a variable can be constant. Thus const var. No shudders!Jermaine
@bradgonesurfing: That wording is terrible, and it appears nowhere in the C# specification. It looks like you found it here: msdn.microsoft.com/en-us/vstudio/e6w8fe1b(v=vs.103).aspx yes? I'll have the documentation manager fix it. Thanks!Arvind
That's the link. However I reiterate we are discussing orthognal issues. It's unfortunate that var means assign and nonconst at the same time and that has little to do with RHS expression type inference.Jermaine
But in const var x = …, the type varies, not the value…Connel
I feel this is implemented better in C++, where auto keyword is somewhat akin to C#'s var and tells the compiler to give the object being declared the same type as the expression on the right of =, but you can combine auto with type modifiers like const, * (pointer) and & (reference). This const vs var antagonism is very confusing for a habitual C++ developer, and it makes declaring things that I know should not be mutated a pain. const is a great way to add a little bit of compiler-enforced sanity checking, it shouldn't be this hard to use!Aquino
C
21

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.

Camorra answered 10/4, 2010 at 19:21 Comment(2)
Agreed. If var can infer the type, why not const?Pushkin
Indeed, that would allow for less redundancy in the code.Hibiscus
A
12

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.

Anestassia answered 24/1, 2010 at 20:21 Comment(3)
Would it then be correct to say that since the referenced values of constants are simply embedded as-is (much like #defines in C), the compiler shouldn't even need to infer the type? (Obviously you would need type inference for IntelliSense.) If so, wouldn't it then be fairly simple to change const to support the syntax desired by the question? Would any compiler changes even be necessary?Pushkin
@IanKemp: It would not be correct. Multiplying an int by an int constant is very different from multiplying an int by a long constant whose value could fit within an int.Interinsurance
Oh, this is the thing I forget. const is like a substituted rather than something that can be initialized to a dynamic value at runtime like in JavaScript or C. So, except for using and foreach variables, C# doesn’t give you a way to mark a local variable immutable.Connel
T
8

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.

Torey answered 24/1, 2010 at 19:40 Comment(3)
ah looks like you beat me to it - I think this is enough of an explanation - 'since there's no reason to do what you're doing, what you're doing is wrong, and it's the job of the compiler to stop you from doing things like that :p'Ceroplastics
So then, following your argument, why doesn't the compiler stop me when I do: var s = "abc"; ?Poyssick
It's their argument too, "If the value of the variable never changes, just give it an explicit type.", see below.Ceroplastics
T
4

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 )

Tbilisi answered 21/10, 2011 at 0:55 Comment(2)
I agree with your reasoning. But to say var doesn't mean what its creator meant while writing it, it takes balls... May be you meant shouldn't :)Pokey
@Pokey you are right!, but, ... ...I was referring to the following sentence written by Eric Lippert. 'The "var" syntax was chosen to call out "this is a variable", and we're sticking with it.' The meaning of "var" according to its "author" is uncertain. ECMA-334 is outdated, and, I wonder if the following web page can be considered as a specification: msdn.microsoft.com/en-us/library/bb383973.aspxTbilisi
N
4

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.

Nineteen answered 24/5, 2012 at 18:55 Comment(0)
I
3

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.

Interinsurance answered 25/1, 2013 at 4:14 Comment(10)
This is a good point, even though you can still indicate types with a suffix (long is 0L, double is default for anything with a ., float is 0F, decimal is 0M, int is anything without a .). Granted, saying const s = 25000L isn't really much easier than const long s = 25000, and you're still specifying the type anyway.Missilery
@drzaus: For values defined purely as numeric literals, a type suffix might help, but if a constants is defined in terms of an expressions, a slight change to that expression could accidentally change the type of the constant. BTW, I think the rules about float and double casts are backward. If legal, the statement float foo = 0.1; would have exactly the intended effect, but const float ten=10.0f; double wrong = 1 / ten; would not, even if though ten holds a precise float value. It irks me that the wrong one doesn't even yield a warning, but the right one won't compile.Interinsurance
I agree, but you are not forced to use implicit typing. If you want that type of a constant to be explicit, you can always write it explicitly and the compiler will check for inconsistencies. I don't see how this is different for constants compared to variables.Hyrax
@Lensflare: Local variables are necessarily used in the same context as they are declared, so whoever is modifying the definition of an implicitly-typed local variable will be able to see whether the type matters and what it needs to be. Constants are often used in places far distant from their definition; while it might make sense to allow locally-scoped constants, and allow type inference with those, the benefits of local constants over local variables would usually be pretty slight.Interinsurance
@supercat: I think it only matters whether we have local or global context. Variables and constants can be both, global and local. You should decide where it is appropriate to use type inference but a language should not restrict it for constants or globals.Hyrax
@Lensflare: If there were a means of declaring const values which were local to a function, then there would be no reason those shouldn't have the same inference rules as var. Allowing var with any kind of public member, however, would introduce some substantial complexity unless there were severe restrictions placed on its usage. Personally I would like to see var allowed--even with public members--with certain forms of right-hand expressions whose type could be ascertained without having to look at outside code; if such a rule had been applied even to local variables...Interinsurance
...then allowing public fields, constants, or properties to infer their type the same way would be no problem. As it is, however, allowing public members to use var with the same kinds of expressions as local variables can would be unworkable, and allowing it only with a more restricted set of rules would be confusing. Note also that var is primarily useful when the right-hand operator's name is a long generic type; the longest possible type name for a valid C# const expression would be Decimal.Interinsurance
@supercat: Yes, in some cases type inference is just undoable because it creates a kind of cyclic dependency. And even for cases where it is doable but overly complex, I am fine if C# forbids it just for consistency. But going back to the original statement, I suggest that wherever it's ok to use var for type inference, if you change it to a constant, it should still be ok to use type inference. C# forbids that appearently. I think that the reason for that is a technical or syntactic limitation and not because C# tells you that you shouldn't.Hyrax
@Lensflare: I don't think C# allows local constants, nor does it allow type inference with things that aren't locals. I don't think any "special rule" is needed to prohibit type inference with constants--the restriction stems from the other two rules.Interinsurance
@supercat: local constants are allowed in C# (as long as they are assigned a constant value like an integer literal). Type inference would not shorten the line drastically, but type inferece is not only useful for brevity and readability, but also to reduce redundancy. I feel uncomfortable writing the same type twice when it is obvious that it should be the same type.Hyrax
C
2

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.

Ceroplastics answered 24/1, 2010 at 19:57 Comment(2)
But with var s = "abc";, the type is also know but there is no compiler error because the type is inferred. So your answer doesn't really answer my question.Poyssick
I see your point, but that's not const -- if they prevented var s = "abc", then obviously none of the implicit typing would be possible. A const var = would clear never change, so it's clearly not what they wanted it to be used for, so be explicit--that's what the doc says. Implicit typing was added to make certain scenarios easier--namely linq. The language designers wanted to prevent dev's from writing bad code (eg const var) while making verbose types like IEnumerable<Foo<Bar>> = ... easier to deal with. In the end I don't think this is a limitation of the CLR, though.Ceroplastics
C
2

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.

Connel answered 21/10, 2016 at 16:44 Comment(0)
S
1

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.

Stroh answered 24/1, 2010 at 19:55 Comment(0)
T
0

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.

Trinitrophenol answered 24/1, 2010 at 19:39 Comment(3)
That doesn't explain why, you are merely repeating the question.Stroh
You answer also doesn't explain why. This is an overuse of var which was created mainly for anonymous types.Trinitrophenol
Beku, that's not the point though. I'm merely asking because I'm curious about the decision of the Language designers.Poyssick

© 2022 - 2024 — McMap. All rights reserved.