众所周知,int
有ToString()
方法覆盖了ToString()
基类型的方法Object
。
对于以下代码,
int x = 100;
object y = (object)x;
Console.Write(y.ToString());
(1)ToString()
谁会被召唤?整数还是对象?为什么?
(2) 我们如何检查/查看真相?通过任何调试/工具?
Int32.ToString()
会被调用,因为ToString()
实际上是被调用的。
Int32.ToString()
覆盖Object.ToString()
,因此在运行时它有效地替换了Object.ToString()
.
在运行时,y
是一个盒装的int
.
由于值是装箱的,编译器只知道是object
,所以它是对 的常规虚拟调用object.ToString()
,然后它将ToString()
获取结构的覆盖。所以:它是object.ToString()
被调用的,而Int32.ToString()
覆盖是被执行的。
private static void Main()
{
int x = 100;
object y = (object)x;
Console.Write(y.ToString());
}
变成(我的评论):
.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
}
重要的线路在L_000b
; 对 的常规虚拟通话object.ToString()
。
更有趣的是非装箱值类型;如果已知值类型具有ToString()
,则它可以发出静态调用:
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
}
请参阅 处的静态调用 ( call
) L_0005
。但是,在大多数值类型的情况下,它将使用受约束的调用,如果它被覆盖,JIT 会将其解释为静态调用,如果不是,则将其解释为虚拟调用:
private static void Main()
{
var x = new KeyValuePair<int, string>(123, "abc");
Console.Write(x.ToString());
}
变成:
.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
}
“约束”/“callvirt”对L_0010
共同构成L_0016
了这个构造,因此它实际上可能不是虚拟调用。JIT / 运行时可以对其进行其他巫术。这将在此处进行更多讨论。
请注意,常规class
将始终为此使用虚拟调用,但场景除外return base.ToString();
,它是对基本类型实现的静态调用。
Int32,因为这是满足相同合同的实际对象。您可以通过查看输出来检查。如果不是这种情况,您将返回“System.Object”。
以为是java。对不起!:) int 是原始类型。所有原始类型都是 OO 魔法停止的唯一例外。它们不继承对象,它们不是对象!(没有 toString 方法,一般没有方法)
整数,具有 toString 方法。如果您的示例使用 Integer 而不是 int,并且您的对象被强制转换为一个类(就像您在示例中所做的那样),如果该类覆盖 toString(例如是您的自定义类),或者已经有了 toString 方法(例如 Integer class),则应该调用该类的 toString 方法。
否则,应该调用继承自的类的 toString 方法。如果没有继承自nobody(意味着:继承自Object 类),则应该调用object 类toString。
编写一个带有一些打印的简单程序可以帮助您:
Integer x = new Integer(3);
打印:
x.toString()
((Object) x).toString();
和新的匿名类,和 Integer 一样,但是 toString 被重写了
Integer y = new Integer(3){
@Override
String toString(){
return "WHATEVER!!";
}
};
打印:
y.toString();