Does var keyword in C# cause boxing?
My boss forbids me to us开发者_Python百科e var
as it would cause boxing and slowing down the app.
Is that true?
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 :)
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
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.
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.
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...
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.
精彩评论