2

In C# why I can call

var intStr = 1.ToString();
var strStr = "1".ToString();

1 and "1" are constant literals so why object.ToString() is not causing errors?

Shibli
  • 5,879
  • 13
  • 62
  • 126
Sandeep Kumar
  • 1,505
  • 1
  • 15
  • 24
  • 3
    Why should they cause errors? Why would it need to be any different than a variable of the exact same type? Also, `ToString` is not an extension method, it's an instance method. *Big* difference. – Servy Mar 13 '14 at 13:57
  • @Servy I was in false impression that constants are different than variables :) and thanks for pointing out that ToString() is an instance method. – Sandeep Kumar Mar 13 '14 at 14:14
  • 1
    Constants *are* different from variables. Variables are *storage locations* and their values can *change*, which is why they are called "variables". Constants are *values* that do not change, which is why they are called "constants". What is unclear from your question is why you believe that the *constancy of the storage of the value* is relevant to *whether it is legal to call a method with the value as the receiver*. Since you've not told us that, I'll hypothesize why you might believe this to be incorrect in my answer. – Eric Lippert Mar 13 '14 at 15:42

2 Answers2

7

Why I can call object extension methods on constants?

Those aren't extension methods.

1 and "1" are constants so why ToString() is not causing errors?

Your question asks "why not" without ever explaining why you think this should be a problem in the first place. When asking questions of this form in the future please say why you think the operation ought to be disallowed.

So let's try to read your mind. Here's a question you could have asked:

1 and "1" are literals but the left hand side of a dot must be a symbol, like x, so why is 1.ToString() not causing errors?

The supposition is incorrect. The thing on the left hand side of a member access dot must be an expression. There are restrictions on that expression; for example, it cannot be a void-returning method call. But there is no restriction on it being a literal.

Here's another question you could have asked:

1 is a constant of value type but the receiver of a method of a value type must be a variable, because the this in the method is a ref to a variable. So why is 1.ToString() not causing errors?

If the receiver of a method call is of a value type and the this has to be of ref type but the receiver expression is not classified as a variable then the compiler copies the value to a temporary variable and passes a ref to that variable.

This means that if the method mutates the variable, the mutation is lost because it is performed on a copy. This is yet another reason why mutable value types are a bad practice. It is easy to lose a mutation accidentally!

Here's another question you could have asked:

1. is the beginning of a float, double or decimal literal; the thing that comes next should be a digit. So why is 1.ToString() not causing errors?

The question makes an incorrect supposition. The lexer checks whether the thing that follows the dot is a digit; if it is then it keeps lexing the literal as a float, double or decimal. (Which it is will be determined by the suffix, if any.) If the thing that follows the dot is not a digit then the lexer lexes the dot as a member access dot and starts lexing a new token for the ToString.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • @EricLippert I'm a beginner in C# and still need to know many concepts. Sorry, and next time I will try to explain my question better for this time here it is. I did not know that in C# constant literals are also treated as object (System.Object) or more clearly previously I believe that constant literals as only values stored in memory and variables points at those values have types. So in the case of var intStr = 1.ToString(); I thought 1 is just 00000000 00000000 00000000 00000001 stored in memory and intStr is a variable of type int and have above binary value as its data. – Sandeep Kumar Mar 14 '14 at 07:59
  • @EricLippert There is nothing wrong in your answer I have learn new things from your answer and also now I know what to think and do when asking a question. :) Thanks – Sandeep Kumar Mar 14 '14 at 08:06
  • 1
    @SandeepKumar: Your belief is completely correct! A constant of value type is just a bit pattern, but *the C# compiler knows that the type is int*. So when you call `1.ToString()` the compiler says "make a temporary variable of type int, copy `1` into that temporary variable, and then invoke `Int32.ToString` with a ref to that variable as `this`." – Eric Lippert Mar 14 '14 at 14:54
5

They are initialised from constant literals (in the program text), but they are actually objects (in that they are logically treated as objects), and all objects implement ToString()

So 1 is of type System.Int32.

Therefore you can call ToString() for them.

The IL generated for this is something like this:

L_0001: ldc.i4.1 
L_0002: stloc.1 
L_0003: ldloca.s CS$0$0000
L_0005: call instance string [mscorlib]System.Int32::ToString()

Note that the ldc.i4.1 is a special instruction that pushes a System.Int32 with value 1 onto the stack. That, specifically, is the instruction that actually "creates" the the System.Int32 value object.

Also note that although System.Int32 is a value type, it is also treated as an object, so that the following statement is always true:

bool isObject = (1 is object);
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • 3
    mm careful ... the literal `1` is an int *value*, which is being boxed to an object prior to having `ToString()` called on it. – CSJ Mar 13 '14 at 14:09
  • 2
    Calling `int.ToString()` doesn't do any boxing - see this answer: http://stackoverflow.com/questions/6423452/boxing-and-unboxing-in-int-and-string Note that if it was boxing, the IL would contain the `box` instruction. It doesn't do boxing for `int` because `int` overrides `ToString()`, which prevents boxing from occurring. – Matthew Watson Mar 13 '14 at 14:21
  • @csj Matthew is correct. The compiler elides the boxing when the value type overrides the method. This is one reason why overriding those methods is a good idea. – Eric Lippert Mar 13 '14 at 14:28
  • @MatthewWatson Did you perhaps post the wrong link? That post doesn't explain why calling `ToString` on an `int` doesn't box it. It talks about both using `ToString` and boxing, but in different contexts. – Servy Mar 13 '14 at 14:36
  • @Servy The reply marked as the answer in the link I gave starts "Calling ToString is not boxing. It creates a new string that just happens to contain the textual representation of your int." - that's what I was alluding to. The question I linked asks if `string s = i.ToString();` does any boxing. – Matthew Watson Mar 13 '14 at 14:54
  • 2
    @CSJ: Fun fact: `1.GetType()` does box the int. `GetType` is not virtual and therefore not overridden on `Int32`, and therefore the `this` must be of type `object`, so it is boxed. Of course you would be silly to do this in the first place as `typeof(int)` is more clear. – Eric Lippert Mar 13 '14 at 15:38