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

时间:2014-02-09 10:31:00

标签: c# clr

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

int i = 0;
i.ToString();

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

2 个答案:

答案 0 :(得分:4)

intSystem.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();
        }
    }
}

被翻译为:

.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

您可以看到没有拳击发生且System.Int32::ToString()被调用。

如果举行拳击比赛,您明确或隐含地将int投放到object。 (请注意,转换为object类型不是拳击发生的唯一情况)

显式转换为object

using System;

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

给出:

.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

通过隐式演员进行拳击:

using System;

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

给出:

.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

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

这是Object.ToString()

的IL代码
.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

答案 1 :(得分:0)

技术上,int继承自System.ValueType,该实体继承自object。但要回答你的问题,没有性能损失。所有价值类型都是密封类型,它们既不是派生的,也不是派生的。因此,虽然正如哈姆雷特哈博伊恩所指出的那样,ToString被int覆盖,它被有效地封存,并且没有虚拟调度,这将需要执行拳击。