does valueType.ToString() does a cast on the valueType?
Asked Answered
O

2

5

lets say, i have the following code in c#

int x = 0;
x.ToString();

does this internally does a boxing of x? Is there a way to see this happening from visual studio?

Ormuz answered 4/9, 2013 at 13:46 Comment(11)
No it doesn't. The ToString implementation on int will return the string representation of the int, which is more a conversion. No casting or boxing is in sight in this instance. As Daniel asks, why do you want to know this? Is there an underlying issue?Dulcie
what do you mean by string representation of int?Ormuz
@AdamHouldsworth - correct but ToString() is defined in System.Object so it is a fair question.Suspicious
and since the original question is for ValueTypes, what about struct ?Ormuz
@HenkHolterman I never said it wasn't fair, I was just trying to make sure there wasn't a larger issue underneath, or an XY problem question.Dulcie
A more interesting question could be probably made for GetType(), because it isn't overridden in value types and is defined only in System.Object.Gimmal
@TijuJohn struct is a value type... unless I'm missing your point?Dulcie
@xanatos: Yes, calling GetType on an int boxes the int. But why would you call GetType on an unboxed int? You already know what type it is. Just say typeof(int) if you need that type object. (If you are calling it on a boxed int then obviously there is no boxing penalty because it's already boxed by assumption.)Unmeaning
@EricLippert I was thinking more in terms of T : struct, but even then it's useless, and GetType() is the only relevant non-virtual method of System.Object, because the others are protected or virtual or special or static.Gimmal
@xanatos: Your analysis is correct. You can say typeof(T) in the generic case and you'll get the type object for the type argument, not the type parameter. Moreover, it is dangerous to say t.GetType() for t of type T (this time assuming no constraint) because T could be int? and t could be null, in which case you box t to null reference and then dereference it to call GetType(), crashing.Unmeaning
@xanatos: Moreover: if T is int? and t is not null then you box t to int and GetType() returns the int type, not the int? type, which is probably unexpected.Unmeaning
G
7

In this specific case, you are using a System.Int32 (an int). That type redefines ToString, Equals and GetHashCode, so no boxing.

If you use a struct that doesn't redefine ToString what you'll have is a constrained callvirt to System.Object.ToString(). The definition of constrained:

When a callvirt method instruction has been prefixed by constrained thisType, the instruction is executed as follows:

  • If thisType is a value type and thisType implements method then ptr is passed unmodified as the 'this' pointer to a call method instruction, for the implementation of method by thisType.
  • If thisType is a value type and thisType does not implement method then ptr is dereferenced, boxed, and passed as the 'this' pointer to the callvirt method instruction.

So there isn't boxing if the value type implements ToString and there is boxing if it doesn't implement it... Interesting. I didn't know.

For non-virtual methods like GetType() that are defined in System.Object the value type is always boxed. Just tested with a:

5.GetType();

resulting IL code:

IL_0001: ldc.i4.5
IL_0002: box [mscorlib]System.Int32
IL_0007: call instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
Gimmal answered 4/9, 2013 at 13:52 Comment(0)
A
6

Here is the IL generated by your code:

IL_0001:  ldc.i4.0    
IL_0002:  stloc.0     // x
IL_0003:  ldloca.s    00 // x
IL_0005:  call        System.Int32.ToString

As you can see no boxing is taking place.

On the other hand, this code

object x = 0;
x.ToString();

will not surprisingly cause boxing:

IL_0001:  ldc.i4.0    
IL_0002:  box         System.Int32
IL_0007:  stloc.0     // x
IL_0008:  ldloc.0     // x
IL_0009:  callvirt    System.Object.ToString

Generally, if if the type of x is not int but any value type (struct) then you have to override ToString to avoid boxing. Specifically, a constrained callvirt is emited:

  • If thisType is a value type and thisType implements method then ptr is passed unmodified as the 'this' pointer to a call method instruction, for the implementation of method by thisType.

  • If thisType is a value type and thisType does not implement method then ptr is dereferenced, boxed, and passed as the 'this' pointer to the callvirt method instruction.

If you want to avoid boxing when calling Equals, GetHashCode and ToString on a value type you need to override these methods.

Ama answered 4/9, 2013 at 13:51 Comment(1)
what will happen if x is a struct?Ormuz

© 2022 - 2024 — McMap. All rights reserved.