C# 中的 var 关键字会导致装箱吗?

发布于 2024-09-16 09:04:38 字数 68 浏览 11 评论 0原文

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

这是真的吗?

My boss forbids me to use var as it would cause boxing and slowing down the app.

Is that true?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

酒绊 2024-09-23 09:04:38

一种可能有效的方法是编写这两个方法:

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

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

编译,并使用 ildasm 来检查生成的 CIL。给你的老板看看。

编辑 @ck 已完成除最后一项之外的所有操作步骤给你:)

An approach that might work is to write these two methods:

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

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

Compile, and use ildasm to examine the produced CIL. Show your boss.

edit @ck has done all but the last step for you :)

甜妞爱困 2024-09-23 09:04:38

根据 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      

Following on from Aakash's answer, here is the IL: (thanks 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      
温柔女人霸气范 2024-09-23 09:04:38

为什么那么多人骂老板是傻子?兄弟们,革命吧!

您的老板需要阅读文档。 var 使编译器通过查看初始化表达式的静态类型来确定变量类型。无论您手动指定类型还是使用 var 并让编译器为您计算出来,在运行时都没有丝毫区别。

更新 在该问题下的评论中,Hans Passant 问道

你能想到任何 var 初始化器吗
这导致拳击不使用
演员阵容?

强制进行此类转换的自包含表达式的一个示例是:

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

但这与:

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

换句话说,这实际上与 var 没有任何关系。我的初始化表达式的结果是 object,因此 var 必须使用它作为变量的类型。不可能是别的什么。

Why are so many people cursed with bosses who are dumb? Revolution, brothers!

Your boss needs to read the documentation. var causes the compiler to figure out the variable type by looking at the static type of the initialization expression. It doesn't make the slightest difference at runtime whether you specify the type by hand or you use var and let the compiler figure it out for you.

Update In a comment under the question, Hans Passant asks

can you think of any var initializer
that causes boxing without using a
cast?

An example of a self-contained expression that forces such a conversion is:

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

But that is just identical to:

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

In other words, this doesn't really have anything to do with var. The result of my initializer expression is object, hence var has to use that as the type of the variable. It couldn't be anything else.

马蹄踏│碎落叶 2024-09-23 09:04:38

这根本不是真的。

var 只是意味着“亲爱的编译器,我知道类型是什么,你也知道,所以让我们继续吧。”

它使代码更短,并且有些人发现这更具可读性(其他人发现它的可读性较差),但没有任何性能损失。

That's not true at all.

var just means "dear compiler, I know what the type is, and so do you, so let's just move on shall we."

It makes the code shorter and some find this more readable (others find it less readable), but there's no performance penalty whatsoever.

浸婚纱 2024-09-23 09:04:38

也许您的老板是一位熟悉 VARIANT 类型的老 Visual Basic(如 <= 6.0)程序员。如果您没有在 DIM 语句中显式指定变量的类型,则它是一个 VARIANT,它是一种 union(如果我)正确回忆。将此类变量传递给函数时,您可以将其视为一种“装箱”和“拆箱”。

有时人们会感到困惑。向您的老板询问他的 Visual Basic 战争故事。倾听、学习并同时赢得一些同情!当您离开办公室时,您可能会指出,c# 编译器在编译时就计算出这些内容,并且“装箱”不再是问题。

不要指望您的老板必须跟上语言/API 的最新变化。这并不是因为愚蠢。这是关于还有其他事情要做。例如,他的工作。

编辑:正如下面的评论所述,告诉您不要出于错误的原因使用 var 可能不是他的工作......

Maybe your boss is an old Visual Basic (as in <= 6.0) programmer used to the VARIANT type. If you didn't specify the type of your variable explicitly in your DIM statement, it was a VARIANT which is a sort of union if I recall correctly. You could view this as a sort of "boxing" and "unboxing" when passing such variables to functions.

Sometimes people get confused. Ask your boss about his Visual Basic war stories. Listen, learn and earn some sympathy at the same time! As you leave the office you could point out that the c# compiler figures this stuff out at compile time and that "boxing" isn't an issue anymore.

Don't expect your boss to have to keep up with the newest changes to languages/APIs. This isn't about being dumb. It's about having other stuff to do. His job, for instance.

Edit: As noted in comments below, though, telling you not to use var for the wrong reasons is probably not his job...

秋凉 2024-09-23 09:04:38

实际上,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 赋值)知道此返回值是 List 内部的值类型(结构),并且可以更有效地使用它 - 即使 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/Enumerator,因此可以优化调用来处理此问题。

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

Actually, var can also avoid boxing in some very specific instances.

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

Results in the following 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

Note that the 2nd one (the var assignment) knows that this return value is a valuetype (struct) from inside List and can more efficiently use it - even though the contract from List.GetEnumerator returns an IEnumerator. This will remove the boxing operation on that struct and results in more efficient code.

This is why, for instance, in the following code the foreach loop and the first using/while pair doesn't cause garbage (due to a lack of boxing) but the 2nd using/while loop does (since it boxes the returned struct):

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())
            {
            }
        }
    }
}

Note also that changing this from a "List" to an "IList" will break this optimization since the IList can only infer that an interface of type IEnumerator is coming back. With the List variable the compiler can be smarter and can see that the only valid return value is a [mscorlib]System.Collections.Generic.List`1/Enumerator and can therefore optimize the call to handle this.

While I understand that this is a very limited case, it may be an important one especially on devices that don't do full incremental garbage collection and pause your threads to do a mark/sweep.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文