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

zeb*_*box 13 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)


        }
Run Code Online (Sandbox Code Playgroud)

在情况(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)
Run Code Online (Sandbox Code Playgroud)

在我明确调用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)
Run Code Online (Sandbox Code Playgroud)

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

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

谁知道为什么?

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

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

Guf*_*ffa 13

你根本没有含蓄地打电话ToString.WriteLine在格式字符串之后接受字符串的方法没有重载,它只接受对象.

所以,你没有暗中调用ToString,你是隐式转换intobject.第一种情况相当于:

Console.WriteLine("val is {0}", (object)val);
Run Code Online (Sandbox Code Playgroud)

由于int是值类型,因此发生装箱.

第二种情况相当于:

Console.WriteLine("val is {0}", (object)val.ToString());
Run Code Online (Sandbox Code Playgroud)

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


Ada*_*son 5

因为在第一个实例中,您在调用函数时将intas传递object给了Console.WriteLine()。这将强制int装箱。在您ToString直接调用的第二种方法中,它避免了装箱并传递了一个stringto WriteLine,它已经是一个引用类型。