代码之家  ›  专栏  ›  技术社区  ›  msfanboy

C中的var关键字是否导致装箱?

  •  38
  • msfanboy  · 技术社区  · 16 年前

    我老板禁止我使用 var 因为它会导致拳击和减慢应用程序的速度。

    是真的吗?

    6 回复  |  直到 12 年前
        1
  •  52
  •   Community Mohan Dere    9 年前

    一种可行的方法是编写这两种方法:

    public static void WithInt()
    {
        int x = 5;
        Console.WriteLine(x);
    }
    
    public static void WithVar()
    {
        var x = 5;
        Console.WriteLine(x);
    }
    

    编译和使用 ildasm 检查生产的CIL。展示你的老板。

    编辑 @ck has done all but the last step 给你:

        2
  •  36
  •   cjk    16 年前

    根据Aakash的回答,这是IL:(谢谢) LINQPad )

    WithInt:
    IL_0000:  ldc.i4.5    
    IL_0001:  stloc.0     
    IL_0002:  ldloc.0     
    IL_0003:  call        System.Console.WriteLine
    IL_0008:  ret         
    
    WithVar:
    IL_0000:  ldc.i4.5    
    IL_0001:  stloc.0     
    IL_0002:  ldloc.0     
    IL_0003:  call        System.Console.WriteLine
    IL_0008:  ret      
    
        3
  •  32
  •   Daniel Earwicker    16 年前

    为什么这么多人被那些愚蠢的老板诅咒?革命,兄弟们!

    你的老板需要阅读文件。 var 使编译器通过查看初始化表达式的静态类型来确定变量类型。无论是手动指定类型还是使用 var 让编译器为您解决这个问题。

    更新 在对这个问题的评论中,汉斯·帕桑特问道

    你能想到任何变量初始值设定项吗 这导致拳击不使用 演员?

    一个自我包含的表达式的例子是:

    var boxedInt = new Func<int, object>(n => n)(5);
    

    但这与:

    object boxedInt = new Func<int, object>(n => n)(5);
    

    换句话说,这与 var . 初始值设定项表达式的结果是 object 因此 var 必须将其用作变量的类型。不可能是别的什么。

        4
  •  32
  •   Brian Rasmussen    16 年前

    这根本不是真的。

    var 只是手段 “亲爱的编译器,我知道这是什么类型,你也知道,我们继续吧,好吗?”

    它使代码变短,一些人发现它更可读(其他人发现它不可读),但没有任何性能损失。

        5
  •  10
  •   Daren Thomas    16 年前

    也许你的老板是一个习惯于 VARIANT 类型。如果没有在 DIM 声明,这是一个 变体 这是一种 union 如果我记错的话。当将这些变量传递给函数时,可以将其视为一种“装箱”和“取消装箱”。

    有时人们会感到困惑。询问你的老板关于他的视觉基础战争故事。同时倾听、学习和获得一些同情!当你离开办公室时,你可以指出C编译器在编译时计算出这些东西,而“装箱”不再是问题了。

    不要指望你的老板必须跟上语言/API的最新变化。这不是为了装傻。这是关于有其他事情要做。例如,他的工作。

    编辑: 但是,正如下面的评论所指出的,告诉你不要使用 var 因为错误的原因可能不是他的工作…

        6
  •  3
  •   Alan Wood    13 年前

    实际上,在一些非常具体的实例中,var也可以避免装箱。

    static void Main(string[] args)
    {
        List<Int32> testList = new List<Int32>();
        IEnumerator<Int32> enumAsInterface = testList.GetEnumerator();
        var enumAsStruct = testList.GetEnumerator();
    }
    

    导致以下IL:

    .method private hidebysig static 
        void Main (
            string[] args
        ) cil managed 
    {
        // Method begins at RVA 0x2050
        // Code size 27 (0x1b)
        .maxstack 1
        .entrypoint
        .locals init (
            [0] class [mscorlib]System.Collections.Generic.List`1<int32> testList,
            [1] class [mscorlib]System.Collections.Generic.IEnumerator`1<int32> enumAsInterface,
            [2] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32> enumAsStruct
        )
    
        IL_0000: nop
        IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()
        IL_0006: stloc.0
        IL_0007: ldloc.0
        IL_0008: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<int32>::GetEnumerator()
        IL_000d: box valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>
        IL_0012: stloc.1
        IL_0013: ldloc.0
        IL_0014: callvirt instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<int32>::GetEnumerator()
        IL_0019: stloc.2
        IL_001a: ret
    } // end of method Program::Main
    

    注意,第二个(var赋值)知道这个返回值是来自内部列表的valuetype(struct),并且可以更有效地使用它——即使来自list.getEnumerator的约定返回一个IEnumerator。这将删除该结构上的装箱操作,并产生更高效的代码。

    这就是为什么,例如,在以下代码中,foreach循环和第一个using/while对不会导致垃圾(由于缺少装箱),但第二个using/while循环会导致垃圾(因为它装箱返回的结构):

    class Program
    {
        static void Main(string[] args)
        {
            List<Int32> testList = new List<Int32>();
    
            foreach (Int32 i in testList)
            {
            }
    
            using (var enumerator = testList.GetEnumerator())
            {
                while (enumerator.MoveNext())
                {
                }
            }
    
            using (IEnumerator<Int32> enumerator = testList.GetEnumerator())
            {
                while (enumerator.MoveNext())
                {
                }
            }
        }
    }
    

    还要注意,将其从“list”更改为“ilist”将破坏此优化,因为ilist只能推断IEnumerator类型的接口正在返回。使用list变量,编译器可以更智能,并且可以看到唯一有效的返回值是[mscorlib]system.collections.generic.list`1/枚举器,因此可以优化调用以处理此问题。

    虽然我了解这是一个非常有限的情况,但它可能是一个重要的情况,尤其是在不进行完全增量垃圾收集和暂停线程以进行标记/扫描的设备上。

    推荐文章