ValueType.ToString和ReferenceType.ToString之间的区别

Arv*_*mar 5 c# clr

两个变量的ToString调用有什么区别?

int i = 0;
i.ToString();
Run Code Online (Sandbox Code Playgroud)

在调用ToString()之前,调用i.ToString()会使我首先装箱然后调用ToString或者我已经装箱了吗?

Boj*_*zec 4

int是结构类型的别名System.Int32(隐式密封),它有一个方法Int32.ToString(),并且这是在代码的第二行中调用的方法,因此不会发生类型转换。

System.Int32源自 源自System.ValueType源自System.ObjectInt32.ToString()覆盖ValueType.ToString()哪个覆盖Object.ToString().

检查是否发生装箱的最好方法是查看IL代码(我一直在使用ILSpy):

using System;

namespace BoxingTest
{
    class Program
    {
        static void Main(string[] args)
        {
            int i = 0;
            string s1 = i.ToString();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

翻译为:

.method private hidebysig static 
    void Main (
        string[] args
    ) cil managed 
{
    // Method begins at RVA 0x2050
    // Code size 12 (0xc)
    .maxstack 1
    .entrypoint
    .locals init (
        [0] int32 i,
        [1] string s1
    )

    IL_0000: nop
    IL_0001: ldc.i4.0
    IL_0002: stloc.0
    IL_0003: ldloca.s i
    IL_0005: call instance string [mscorlib]System.Int32::ToString()
    IL_000a: stloc.1
    IL_000b: ret
} // end of method Program::Main
Run Code Online (Sandbox Code Playgroud)

您可以看到没有发生拳击并且System.Int32::ToString()被判罚了。

如果你把你的投掷intobject。(请注意,转换为object类型并不是装箱发生的唯一情况)

显式转换为类型object

using System;

namespace BoxingTest
{
    class Program
    {
        static void Main(string[] args)
        {
            int i = 0;
            string s2 = ((object)i).ToString();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

给出:

.method private hidebysig static 
    void Main (
        string[] args
    ) cil managed 
{
    // Method begins at RVA 0x2050
    // Code size 16 (0x10)
    .maxstack 1
    .entrypoint
    .locals init (
        [0] int32 i,
        [1] string s2
    )

    IL_0000: nop
    IL_0001: ldc.i4.0
    IL_0002: stloc.0
    IL_0003: ldloc.0
    IL_0004: box [mscorlib]System.Int32
    IL_0009: callvirt instance string [mscorlib]System.Object::ToString()
    IL_000e: stloc.1
    IL_000f: ret
} // end of method Program::Main
Run Code Online (Sandbox Code Playgroud)

通过隐式强制转换进行拳击:

using System;

namespace BoxingTest
{
    class Program
    {
        static void Main(string[] args)
        {
            int i = 0;
            object o = i;
            string s3 = o.ToString();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

给出:

.method private hidebysig static 
    void Main (
        string[] args
    ) cil managed 
{
    // Method begins at RVA 0x2050
    // Code size 18 (0x12)
    .maxstack 1
    .entrypoint
    .locals init (
        [0] int32 i,
        [1] object o,
        [2] string s3
    )

    IL_0000: nop
    IL_0001: ldc.i4.0
    IL_0002: stloc.0
    IL_0003: ldloc.0
    IL_0004: box [mscorlib]System.Int32
    IL_0009: stloc.1
    IL_000a: ldloc.1
    IL_000b: callvirt instance string [mscorlib]System.Object::ToString()
    IL_0010: stloc.2
    IL_0011: ret
} // end of method Program::Main
Run Code Online (Sandbox Code Playgroud)

在所有三种情况下,字符串都有价值"0",因为Object.ToString()知道装箱变量的原始类型并调用该类型的 ToString()`。

这是以下的 IL 代码Object.ToString()

.method public hidebysig newslot virtual 
    instance string ToString () cil managed 
{
    .custom instance void __DynamicallyInvokableAttribute::.ctor() = (
        01 00 00 00
    )
    // Method begins at RVA 0x2052
    // Code size 12 (0xc)
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: call instance class System.Type System.Object::GetType()
    IL_0006: callvirt instance string System.Object::ToString()
    IL_000b: ret
} // end of method Object::ToString
Run Code Online (Sandbox Code Playgroud)