为什么在值类型上隐式调用toString会导致box指令

时间:2009-08-31 23:27:42

标签: c# performance

这是一个'奇怪的原因'而不是一个特定的问题,但请看下面的代码

        static void Main(string[] args)
        {
            int val = 10;

            Console.WriteLine("val is {0}", val); // (1)
            Console.WriteLine("val is {0}", val.ToString()); //(2)


        }

在情况(1)中输出以下IL

IL_0000:  nop
  IL_0001:  ldc.i4.s   10
  IL_0003:  stloc.0
  IL_0004:  ldstr      "val is {0}"
  IL_0009:  ldloc.0
  IL_000a:  box        [mscorlib]System.Int32
  IL_000f:  call       void [mscorlib]System.Console::WriteLine(string,
                                                                object)

在我明确调用toString方法的情况(2)中,我得到了

IL_0014:  nop
  IL_0015:  ldstr      "val is {0}"
  IL_001a:  ldloca.s   val
  IL_001c:  call       instance string [mscorlib]System.Int32::ToString()
  IL_0021:  call       void [mscorlib]System.Console::WriteLine(string,
                                                                object)

所以在case(1)中,即使int重写toString,也会将值类型装箱并调用toString方法,然后可能会调用vtable override

因此结果完全相同,但显式的toString避免了装箱操作

任何人都知道为什么?

=编辑=
可以清楚,令我困惑的是,我开始假设即使int派生自System.ValueType,而System.ValueType又派生自System.Object,因为它包含toString,GetHashCode等。 所以在我的天真视图中(可能来自C ++),如果我覆盖从System.Object派生的方法,那么就不需要强制转换为System.Object(并因此将值类型框),因为存在overriden方法并且编译器将自动引用该类型的vtable条目 我也假设调用Console.WriteLine()隐式调用int.toString所以也许这就是我出错的地方。希望有意义

好的 - 全部排序。谢谢大家直截了当。所有这些都与我的糟糕假设有关,即Console.WriteLine正在进行隐式字符串转换。不要问我为什么这么想 - 现在看来有多么明显错误:)

3 个答案:

答案 0 :(得分:13)

您根本没有暗中调用ToString。 <{1}}方法没有重载,它接受格式字符串后面的字符串,只接受对象。

因此,您并未暗中调用WriteLine,而是隐式将ToString转换为int。第一种情况相当于:

object

由于Console.WriteLine("val is {0}", (object)val); 是值类型,因此发生装箱。

第二种情况相当于:

int

由于字符串是引用类型,将其强制转换为对象实际上不会导致发出任何代码。它只是将类型与方法签名匹配。

答案 1 :(得分:4)

因为在第一个实例中,您在调用int函数时将object作为Console.WriteLine()传递。这会强制int装箱。在第二种方法中,您直接调用ToString,这可以避免装箱,并将string传递给WriteLine,这已经是参考类型。

答案 2 :(得分:2)

在第一次调用中根本没有.ToString调用。而是调用函数Console.WriteLine(object)。第一个参数是int类型,必须加框以满足类型对象。稍后在WriteLite内部,将在对象上调用.ToString。