difference between ValueType.ToString and ReferenceType.ToString [duplicate]

≡放荡痞女 提交于 2021-02-05 04:55:23

问题


What is the difference between two variable's ToString Calling ?

int i = 0;
i.ToString();

Does calling i.ToString() will make i first boxed then call ToString or i is already boxed before calling ToString() ?


回答1:


int is an alias of System.Int32 struct type (implicitly sealed) which has a method Int32.ToString() and this is a method called in the second line of your code so no type conversion occurs.

System.Int32 is derived from System.ValueType which is derived from System.Object. Int32.ToString() overrides ValueType.ToString() which overrides Object.ToString().

The best way to check whether boxing occurs is to see IL code (I've been using ILSpy):

using System;

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

is translated to:

.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

You can see that no boxing occurred and that System.Int32::ToString() was called.

Boxing would have taken place if e.g. you casted your int to object explicitly or implicitly. (Note that conversion to object type is not the only case when boxing happens)

Explicit casting to type object:

using System;

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

gives:

.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

Boxing through implicit cast:

using System;

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

gives:

.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

In all three cases strings will have value "0" because Object.ToString() knows the original type of the boxed variable and calls ToString()` of that type.

This is the IL code of the 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



回答2:


Technically int inherits from System.ValueType which intern inherits from object. But to answer your question, there is no performance penalty. All Value Types are sealed types, they an neither derive nor be derived from. So, although as Hamlet Hakobyan points out, ToString is overridden by int, it is effectively sealed and no virtual dispatch, which would require boxing is performed.



来源:https://stackoverflow.com/questions/21657958/difference-between-valuetype-tostring-and-referencetype-tostring

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!