none
【C#】请教几种类型转换方式的性能 RRS feed

  • 问题

  • 不理安全健壮,只谈性能。

    转换到值类型:
    object a = 3;
    【方式1】int i = (int)a;
    【方式2】i = int.Parse(a.ToString());
    【方式3】int.TryParse(a.ToString(),out i);
    上述哪种最高效?

    转换到引用类型:
    Control a = new Control();
    【方式1】TextBox a = (TextBox)a;
    【方式2】TextBox a = a as TextBox;
    上述哪种最高效?

    Convert类注重的是安全,所以直接排除。


    2012年8月28日 8:16

答案

  • int i = (int)a;最高效。

    TextBox a = (TextBox)a;最高效

    两者不考虑出错情况。

    理由:

    第一种情况object直接拆箱操作转化成int类型即可。而int.TryParse还要检测是否可以转化以及处理转化成功与否。Parse也要一位位判断是否是数字后再决定。

    第二种as只是判断可否转化(不能转化则按照默认值,可以转化才把转化的数值赋值)。但是强制转换拆箱耗时巨大:

    using System;

    using System.Data;

    namespace CSharp

    {

        internal class MainTest

        {

            private static void Main()

            {

                object o = new object();

                DataTable dt = o as DataTable;

            }

        }

    }

    那么转化成IL:

    .class private auto ansi beforefieldinit CSharp.MainTest

        extends [mscorlib]System.Object

    {

        // Methods

        .method private hidebysig static 

            void Main () cil managed 

        {

            // Method begins at RVA 0x2050

            // Code size 15 (0xf)

            .maxstack 1

            .entrypoint

            .locals init (

                [0] object o,

                [1] class [System.Data]System.Data.DataTable dt

            )



            IL_0000: nop

            IL_0001: newobj instance void [mscorlib]System.Object::.ctor()

            IL_0006: stloc.0

            IL_0007: ldloc.0

            IL_0008: isinst [System.Data]System.Data.DataTable

            IL_000d: stloc.1

            IL_000e: ret

        } // end of method MainTest::Main



        .method public hidebysig specialname rtspecialname 

            instance void .ctor () cil managed 

        {

            // Method begins at RVA 0x206b

            // Code size 7 (0x7)

            .maxstack 8



            IL_0000: ldarg.0

            IL_0001: call instance void [mscorlib]System.Object::.ctor()

            IL_0006: ret

        } // end of method MainTest::.ctor


    // end of class CSharp.MainTest

    如果是拆箱:

    .class private auto ansi beforefieldinit CSharp.MainTest

        extends [mscorlib]System.Object

    {

        // Methods

        .method private hidebysig static 

            void Main () cil managed 

        {

            // Method begins at RVA 0x2050

            // Code size 15 (0xf)

            .maxstack 1

            .entrypoint

            .locals init (

                [0] object o,

                [1] class [System.Data]System.Data.DataTable dt

            )



            IL_0000: nop

            IL_0001: newobj instance void [System.Data]System.Data.DataTable::.ctor()

            IL_0006: stloc.0

            IL_0007: ldloc.0

            IL_0008: castclass [System.Data]System.Data.DataTable

            IL_000d: stloc.1

            IL_000e: ret

        } // end of method MainTest::Main



        .method public hidebysig specialname rtspecialname 

            instance void .ctor () cil managed 

        {

            // Method begins at RVA 0x206b

            // Code size 7 (0x7)

            .maxstack 8



            IL_0000: ldarg.0

            IL_0001: call instance void [mscorlib]System.Object::.ctor()

            IL_0006: ret

        } // end of method MainTest::.ctor



    // end of class CSharp.MainTest

    一般castclass需要从堆栈上取下实体,然后转换到目标实体。耗时巨大。


    下载MSDN桌面工具(Vista,Win7)
    我的博客园
    慈善点击,点击此处

    2012年8月28日 8:47
    版主
  • 实验证明:

    namespace CSharp { class MainTest { static void Main() { Stopwatch watch = new Stopwatch(); object o = new DataTable(); DataTable dt = null; watch.Start(); for (int i = 1; i <= 100000000; i++) { dt = o as DataTable;

    } watch.Stop(); Console.WriteLine("as测试:"+watch.ElapsedMilliseconds); watch = new Stopwatch(); watch.Start(); for (int i = 1; i <= 100000000; i++) { dt = (DataTable)o;

    } watch.Stop(); Console.WriteLine("强制转换测试:" + watch.ElapsedMilliseconds); } } }


    下载MSDN桌面工具(Vista,Win7)
    我的博客园
    慈善点击,点击此处

    2012年8月28日 9:03
    版主

全部回复

  • int i = (int)a;最高效。

    TextBox a = (TextBox)a;最高效

    两者不考虑出错情况。

    理由:

    第一种情况object直接拆箱操作转化成int类型即可。而int.TryParse还要检测是否可以转化以及处理转化成功与否。Parse也要一位位判断是否是数字后再决定。

    第二种as只是判断可否转化(不能转化则按照默认值,可以转化才把转化的数值赋值)。但是强制转换拆箱耗时巨大:

    using System;

    using System.Data;

    namespace CSharp

    {

        internal class MainTest

        {

            private static void Main()

            {

                object o = new object();

                DataTable dt = o as DataTable;

            }

        }

    }

    那么转化成IL:

    .class private auto ansi beforefieldinit CSharp.MainTest

        extends [mscorlib]System.Object

    {

        // Methods

        .method private hidebysig static 

            void Main () cil managed 

        {

            // Method begins at RVA 0x2050

            // Code size 15 (0xf)

            .maxstack 1

            .entrypoint

            .locals init (

                [0] object o,

                [1] class [System.Data]System.Data.DataTable dt

            )



            IL_0000: nop

            IL_0001: newobj instance void [mscorlib]System.Object::.ctor()

            IL_0006: stloc.0

            IL_0007: ldloc.0

            IL_0008: isinst [System.Data]System.Data.DataTable

            IL_000d: stloc.1

            IL_000e: ret

        } // end of method MainTest::Main



        .method public hidebysig specialname rtspecialname 

            instance void .ctor () cil managed 

        {

            // Method begins at RVA 0x206b

            // Code size 7 (0x7)

            .maxstack 8



            IL_0000: ldarg.0

            IL_0001: call instance void [mscorlib]System.Object::.ctor()

            IL_0006: ret

        } // end of method MainTest::.ctor


    // end of class CSharp.MainTest

    如果是拆箱:

    .class private auto ansi beforefieldinit CSharp.MainTest

        extends [mscorlib]System.Object

    {

        // Methods

        .method private hidebysig static 

            void Main () cil managed 

        {

            // Method begins at RVA 0x2050

            // Code size 15 (0xf)

            .maxstack 1

            .entrypoint

            .locals init (

                [0] object o,

                [1] class [System.Data]System.Data.DataTable dt

            )



            IL_0000: nop

            IL_0001: newobj instance void [System.Data]System.Data.DataTable::.ctor()

            IL_0006: stloc.0

            IL_0007: ldloc.0

            IL_0008: castclass [System.Data]System.Data.DataTable

            IL_000d: stloc.1

            IL_000e: ret

        } // end of method MainTest::Main



        .method public hidebysig specialname rtspecialname 

            instance void .ctor () cil managed 

        {

            // Method begins at RVA 0x206b

            // Code size 7 (0x7)

            .maxstack 8



            IL_0000: ldarg.0

            IL_0001: call instance void [mscorlib]System.Object::.ctor()

            IL_0006: ret

        } // end of method MainTest::.ctor



    // end of class CSharp.MainTest

    一般castclass需要从堆栈上取下实体,然后转换到目标实体。耗时巨大。


    下载MSDN桌面工具(Vista,Win7)
    我的博客园
    慈善点击,点击此处

    2012年8月28日 8:47
    版主
  • 实验证明:

    namespace CSharp { class MainTest { static void Main() { Stopwatch watch = new Stopwatch(); object o = new DataTable(); DataTable dt = null; watch.Start(); for (int i = 1; i <= 100000000; i++) { dt = o as DataTable;

    } watch.Stop(); Console.WriteLine("as测试:"+watch.ElapsedMilliseconds); watch = new Stopwatch(); watch.Start(); for (int i = 1; i <= 100000000; i++) { dt = (DataTable)o;

    } watch.Stop(); Console.WriteLine("强制转换测试:" + watch.ElapsedMilliseconds); } } }


    下载MSDN桌面工具(Vista,Win7)
    我的博客园
    慈善点击,点击此处

    2012年8月28日 9:03
    版主
  • 在我这儿测试,as始终要比直接转换要多用3~4秒。非常感谢您的耐心讲解!
    2012年8月30日 5:23
  • 在我这儿测试,as始终要比直接转换要多用3~4秒。非常感谢您的耐心讲解!

    ???如果这样,先不要标记……你给出你的代码?

    下载MSDN桌面工具(Vista,Win7)
    我的博客园
    慈善点击,点击此处

    2012年8月30日 5:28
    版主
  • 就是用你的代码。请看:

    我试了应该有10次,通常是相差3、4秒,这5秒的较少。不说()比as快吗?不正常吗?

    单位错了,是毫秒

    2012年8月31日 0:57
  • 看来与项目.net版本有关,2.0下as始终要多用时,但到了4.5下,as比()快很多。看来在不同框架下,编译成的IL可能有异,在旧版下的一些定论到了新版可能已被推翻~这个问题值得研究
    2012年9月1日 7:40
  • 看来与项目.net版本有关,2.0下as始终要多用时,但到了4.5下,as比()快很多。这个问题值得研究

    哦?你做过例子?

    下载MSDN桌面工具(Vista,Win7)
    我的博客园
    慈善点击,点击此处

    2012年9月1日 7:42
    版主
  • 是的,把项目框架改为4.5,再改回2.0,再改到4.5……反复尝试得出上述结论,中间的3.0/3.5/4.0没尝试,但足以说明CLR面对不同框架的程序集有不同的表现。
    2012年9月2日 3:01
  • DEBUG(4.0):

    RELEASE(4.0):

    2012年9月4日 5:49
  • DEBUG 下强制转换比较快,RELEASE 下 As 比较快,我测试的框架是4.0

    2012年9月4日 5:50
  • 看来在as和()之间,因素太多了,不好定论
    2012年9月4日 16:01