5

众所周知,intToString()方法覆盖了ToString()基类型的方法Object

对于以下代码,

int x = 100;
object y = (object)x;
Console.Write(y.ToString());

(1)ToString()谁会被召唤?整数还是对象?为什么?
(2) 我们如何检查/查看真相?通过任何调试/工具?

4

4 回答 4

4

Int32.ToString()会被调用,因为ToString()实际上是被调用的。 Int32.ToString()覆盖Object.ToString(),因此在运行时它有效地替换了Object.ToString().
在运行时,y是一个盒装的int.

于 2012-08-02T06:28:11.483 回答
3

由于值是装箱的,编译器只知道是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();,它是对基本类型实现的静态调用。

于 2012-08-02T07:44:31.977 回答
2

Int32,因为这是满足相同合同的实际对象。您可以通过查看输出来检查。如果不是这种情况,您将返回“System.Object”。

于 2012-08-02T06:29:28.800 回答
0

以为是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();
于 2012-08-02T06:32:18.780 回答