Como sabemos, int
tem ToString()
método que substitui o ToString()
método do tipo base Object
.
Para este código a seguir,
int x = 100;
object y = (object)x;
Console.Write(y.ToString());
(1) De quem ToString()
seria chamado? int ou objeto? PORQUE?
(2) Como podemos verificar / ver a verdade? Por qualquer depuração / ferramenta?
Respostas:
3 para resposta № 1Como o valor está na caixa, tudo o que o compilador conhece é object
, portanto, é uma chamada virtual regular para object.ToString()
, que selecionará a opção substituída ToString()
da estrutura. Então é object.ToString()
isso é invocado, e as Int32.ToString()
sobrepor que é executado.
private static void Main()
{
int x = 100;
object y = (object)x;
Console.Write(y.ToString());
}
torna-se (comenta o meu):
.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
}
A linha importante está em L_000b
; uma chamada virtual regular para object.ToString()
.
O que fica mais interessante é sem caixa tipos de valor; se um tipo de valor for conhecido ter um ToString()
, ele pode emitir uma chamada estática:
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
}
Veja a chamada estática (call
) em L_0005
. CONTUDO, na maioria dos casos de tipo de valor, ele usará um constrangido chamada, que será interpretada pelo JIT como uma chamada estática se é substituído e uma chamada virtual se isn "t:
private static void Main()
{
var x = new KeyValuePair<int, string>(123, "abc");
Console.Write(x.ToString());
}
torna-se:
.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
}
O par "restrito" / "chamada de chamada" em L_0010
e L_0016
juntos faça essa construção, para que não na realidade ser uma chamada virtual. O JIT / tempo de execução pode fazer outro vodu nele. Isto é discutido mais aqui.
Observe que um regular class
vai sempre use chamada virtual para isso, exceto para o cenário return base.ToString();
, que é uma chamada estática para a implementação dos tipos de base.
4 para resposta № 2
Int32.ToString()
seria chamado, porque ToString()
é chamado virtualmente.
Int32.ToString()
substitui Object.ToString()
, portanto, em tempo de execução, ele substitui efetivamente Object.ToString()
.
E em tempo de execução, y
é uma caixa int
.
2 para resposta № 3
Int32, porque esse é o objeto real abaixo do cumprimento do mesmo contrato. Você pode verificar observando a saída. Se esse não fosse o caso, você teria "System.Object" retornado.
0 para a resposta № 4
Pensei que era java. Desculpe! :) int é um tipo primitivo. Todos os tipos primitivos são as únicas exceções em que a magia do OO para. Eles não herdam objetos, não são objetos! (Nenhum método toString, nenhum método em geral)
Inteiro, tem o método toString. Se seu exemplo foi com Integer, em vez de int, e seu objeto foi convertido para uma classe (como você fez no exemplo), se essa classe substitui toString (por exemplo, era sua classe personalizada) ou apenas o método toString (como Integer, por exemplo) class), esse método toString da classe deve ser chamado.
Caso contrário, o método toString da classe que herdou de deve ser chamado. Se não herdou de ninguém (significa: herdado da classe Object), a classe de objeto toString deve ser chamada.
Escrever um programa simples com algumas impressões pode ajudá-lo:
Integer x = new Integer(3);
impressão:
x.toString()
((Object) x).toString();
E nova classe anônima, igual a Inteiro, mas paraString Overriden
Integer y = new Integer(3){
@Override
String toString(){
return "WHATEVER!!";
}
};
impressão:
y.toString();