Reputation: 205
As we know, int
has ToString()
method which override the ToString()
method of base type Object
.
For this following code,
int x = 100;
object y = (object)x;
Console.Write(y.ToString());
(1) Whose ToString()
would be called? int or object? WHY?
(2) How we can check/view the truth? By any debug/tool?
Upvotes: 5
Views: 166
Reputation: 1063338
Since the value is boxed, all the compiler knows about is object
, so it is a regular virtual call to object.ToString()
, which will then pick up the overridden ToString()
of the struct. So: it is object.ToString()
that is invoked, and the Int32.ToString()
override which is executed.
private static void Main()
{
int x = 100;
object y = (object)x;
Console.Write(y.ToString());
}
becomes (comments mine):
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 1
.locals init (
[0] int32 x,
[1] object y)
// int x = 100;
L_0000: ldc.i4.s 100
L_0002: stloc.0
// object y = (object)x;
L_0003: ldloc.0
L_0004: box int32
L_0009: stloc.1
// Console.Write(y.ToString());
L_000a: ldloc.1
L_000b: callvirt instance string [mscorlib]System.Object::ToString()
L_0010: call void [mscorlib]System.Console::Write(string)
L_0015: ret
}
The important line is at L_000b
; a regular virtual-call to object.ToString()
.
What gets more interesting is non-boxed value-types; if a value-type is known to have a ToString()
, then it can emit a static call:
private static void Main()
{
int x = 100;
Console.Write(x.ToString());
}
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 1
.locals init (
[0] int32 x)
L_0000: ldc.i4.s 100
L_0002: stloc.0
L_0003: ldloca.s x
L_0005: call instance string [mscorlib]System.Int32::ToString()
L_000a: call void [mscorlib]System.Console::Write(string)
L_000f: ret
}
See the static-call (call
) at L_0005
. HOWEVER, in most value-type cases it will use a constrained call, which will be interpreted by the JIT as a static-call if it is overridden, and a virtual-call if it isn't:
private static void Main()
{
var x = new KeyValuePair<int, string>(123, "abc");
Console.Write(x.ToString());
}
becomes:
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 3
.locals init (
[0] valuetype [mscorlib]System.Collections.Generic.KeyValuePair`2<int32, string> x)
L_0000: ldloca.s x
L_0002: ldc.i4.s 0x7b
L_0004: ldstr "abc"
L_0009: call instance void [mscorlib]System.Collections.Generic.KeyValuePair`2<int32, string>::.ctor(!0, !1)
L_000e: ldloca.s x
L_0010: constrained [mscorlib]System.Collections.Generic.KeyValuePair`2<int32, string>
L_0016: callvirt instance string [mscorlib]System.Object::ToString()
L_001b: call void [mscorlib]System.Console::Write(string)
L_0020: ret
}
The "constrained" / "callvirt" pair at L_0010
and L_0016
together make this construct, so it might not actually be a virtual call. The JIT / runtime can do other voodoo on it. This is discussed more here.
Note that a regular class
will always use virtual call for this, except for the scenario return base.ToString();
, which is a static-call to the base-types implementation.
Upvotes: 3
Reputation: 12301
Thought that was java. Sorry! :) int is a primitive type. All primitive types, are the only exceptions where the magic of OO stops. They dont inherit object, they are not objects!(no toString method, no methods in general)
Integer, has the toString method. If your example was with Integer, instead of int, and your object was casted to a class(like you did in your example), if that class overrides toString(eg was your custom class), or just already had toString method(like Integer class), that class toString method should be called.
Otherwise, the toString method of the class who inherited from should be called. If didnt inherited from nobody(means: inherited from Object class), object class toString should be called.
Writing a simple program with some prints can help you:
Integer x = new Integer(3);
print:
x.toString()
((Object) x).toString();
And new anonymous class, same as Integer, but toString Overriden
Integer y = new Integer(3){
@Override
String toString(){
return "WHATEVER!!";
}
};
print:
y.toString();
Upvotes: 0
Reputation: 39620
Int32.ToString()
would be called, because ToString()
is called virtually.
Int32.ToString()
overrides Object.ToString()
, so at runtime it effectively replaces Object.ToString()
.
And at runtime, y
is a boxed int
.
Upvotes: 4
Reputation: 129654
Int32, because that's the actual object underneath satisfying the same contract. You can check by watching the output. If this wouldn't be the case you would have "System.Object" returned instead.
Upvotes: 2