I am trying to figure out whether a boxing occurs when you access an integer as a property of dynamic
type
Here is the C# code:
using System.Dynamic;
dynamic o = new ExpandoObject();
int i = 2;
o.Int = 1;
i = o.Int;
Console.WriteLine(o.Int);
Console.WriteLine(i);
Here is what I get from ILSpy:
.method private hidebysig static
void '<Main>$' (
string[] args
) cil managed
{
// Method begins at RVA 0x206c
// Header size: 12
// Code size: 428 (0x1ac)
.maxstack 11
.entrypoint
.locals init (
[0] object o,
[1] int32 i
)
// dynamic val = new ExpandoObject();
IL_0000: newobj instance void [System.Linq.Expressions]System.Dynamic.ExpandoObject::.ctor()
IL_0005: stloc.0
// int num = 2;
IL_0006: ldc.i4.2
IL_0007: stloc.1
// (no C# code)
IL_0008: ldsfld class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`4<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, int32, object>> Program/'<>o__0'::'<>p__0'
IL_000d: brfalse.s IL_0011
IL_000f: br.s IL_004a
IL_0011: ldc.i4.0
IL_0012: ldstr "Int"
IL_0017: ldtoken Program
IL_001c: call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
IL_0021: ldc.i4.2
IL_0022: newarr [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo
IL_0027: dup
IL_0028: ldc.i4.0
IL_0029: ldc.i4.0
IL_002a: ldnull
IL_002b: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)
IL_0030: stelem.ref
IL_0031: dup
IL_0032: ldc.i4.1
IL_0033: ldc.i4.3
IL_0034: ldnull
IL_0035: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)
IL_003a: stelem.ref
IL_003b: call class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSiteBinder [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.Binder::SetMember(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, class [System.Runtime]System.Type, class [System.Runtime]System.Collections.Generic.IEnumerable`1<class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)
IL_0040: call class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<!0> class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`4<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, int32, object>>::Create(class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSiteBinder)
IL_0045: stsfld class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`4<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, int32, object>> Program/'<>o__0'::'<>p__0'
// val.Int = 1;
IL_004a: ldsfld class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`4<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, int32, object>> Program/'<>o__0'::'<>p__0'
IL_004f: ldfld !0 class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`4<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, int32, object>>::Target
IL_0054: ldsfld class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`4<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, int32, object>> Program/'<>o__0'::'<>p__0'
IL_0059: ldloc.0
IL_005a: ldc.i4.1
IL_005b: callvirt instance !3 class [System.Runtime]System.Func`4<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, int32, object>::Invoke(!0, !1, !2)
// (no C# code)
IL_0060: pop
IL_0061: ldsfld class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, int32>> Program/'<>o__0'::'<>p__2'
IL_0066: brfalse.s IL_006a
IL_0068: br.s IL_008e
IL_006a: ldc.i4.0
IL_006b: ldtoken [System.Runtime]System.Int32
IL_0070: call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
IL_0075: ldtoken Program
IL_007a: call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
IL_007f: call class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSiteBinder [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.Binder::Convert(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, class [System.Runtime]System.Type, class [System.Runtime]System.Type)
IL_0084: call class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<!0> class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, int32>>::Create(class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSiteBinder)
IL_0089: stsfld class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, int32>> Program/'<>o__0'::'<>p__2'
// num = val.Int;
IL_008e: ldsfld class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, int32>> Program/'<>o__0'::'<>p__2'
IL_0093: ldfld !0 class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, int32>>::Target
IL_0098: ldsfld class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, int32>> Program/'<>o__0'::'<>p__2'
IL_009d: ldsfld class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, object>> Program/'<>o__0'::'<>p__1'
IL_00a2: brfalse.s IL_00a6
IL_00a4: br.s IL_00d5
IL_00a6: ldc.i4.0
IL_00a7: ldstr "Int"
IL_00ac: ldtoken Program
IL_00b1: call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
IL_00b6: ldc.i4.1
IL_00b7: newarr [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo
IL_00bc: dup
IL_00bd: ldc.i4.0
IL_00be: ldc.i4.0
IL_00bf: ldnull
IL_00c0: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)
IL_00c5: stelem.ref
IL_00c6: call class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSiteBinder [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.Binder::GetMember(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, class [System.Runtime]System.Type, class [System.Runtime]System.Collections.Generic.IEnumerable`1<class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)
IL_00cb: call class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<!0> class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, object>>::Create(class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSiteBinder)
IL_00d0: stsfld class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, object>> Program/'<>o__0'::'<>p__1'
IL_00d5: ldsfld class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, object>> Program/'<>o__0'::'<>p__1'
IL_00da: ldfld !0 class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, object>>::Target
IL_00df: ldsfld class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, object>> Program/'<>o__0'::'<>p__1'
IL_00e4: ldloc.0
IL_00e5: callvirt instance !2 class [System.Runtime]System.Func`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, object>::Invoke(!0, !1)
IL_00ea: callvirt instance !2 class [System.Runtime]System.Func`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, int32>::Invoke(!0, !1)
IL_00ef: stloc.1
// (no C# code)
IL_00f0: ldsfld class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Action`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, class [System.Runtime]System.Type, object>> Program/'<>o__0'::'<>p__4'
IL_00f5: brfalse.s IL_00f9
IL_00f7: br.s IL_0138
IL_00f9: ldc.i4 256
IL_00fe: ldstr "WriteLine"
IL_0103: ldnull
IL_0104: ldtoken Program
IL_0109: call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
IL_010e: ldc.i4.2
IL_010f: newarr [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo
IL_0114: dup
IL_0115: ldc.i4.0
IL_0116: ldc.i4.s 33
IL_0118: ldnull
IL_0119: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)
IL_011e: stelem.ref
IL_011f: dup
IL_0120: ldc.i4.1
IL_0121: ldc.i4.0
IL_0122: ldnull
IL_0123: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)
IL_0128: stelem.ref
IL_0129: call class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSiteBinder [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.Binder::InvokeMember(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, class [System.Runtime]System.Collections.Generic.IEnumerable`1<class [System.Runtime]System.Type>, class [System.Runtime]System.Type, class [System.Runtime]System.Collections.Generic.IEnumerable`1<class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)
IL_012e: call class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<!0> class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Action`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, class [System.Runtime]System.Type, object>>::Create(class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSiteBinder)
IL_0133: stsfld class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Action`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, class [System.Runtime]System.Type, object>> Program/'<>o__0'::'<>p__4'
// Console.WriteLine(val.Int);
IL_0138: ldsfld class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Action`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, class [System.Runtime]System.Type, object>> Program/'<>o__0'::'<>p__4'
IL_013d: ldfld !0 class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Action`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, class [System.Runtime]System.Type, object>>::Target
IL_0142: ldsfld class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Action`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, class [System.Runtime]System.Type, object>> Program/'<>o__0'::'<>p__4'
IL_0147: ldtoken [System.Console]System.Console
IL_014c: call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
IL_0151: ldsfld class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, object>> Program/'<>o__0'::'<>p__3'
IL_0156: brfalse.s IL_015a
IL_0158: br.s IL_0189
IL_015a: ldc.i4.0
IL_015b: ldstr "Int"
IL_0160: ldtoken Program
IL_0165: call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
IL_016a: ldc.i4.1
IL_016b: newarr [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo
IL_0170: dup
IL_0171: ldc.i4.0
IL_0172: ldc.i4.0
IL_0173: ldnull
IL_0174: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)
IL_0179: stelem.ref
IL_017a: call class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSiteBinder [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.Binder::GetMember(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, class [System.Runtime]System.Type, class [System.Runtime]System.Collections.Generic.IEnumerable`1<class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)
IL_017f: call class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<!0> class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, object>>::Create(class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSiteBinder)
IL_0184: stsfld class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, object>> Program/'<>o__0'::'<>p__3'
IL_0189: ldsfld class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, object>> Program/'<>o__0'::'<>p__3'
IL_018e: ldfld !0 class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, object>>::Target
IL_0193: ldsfld class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite`1<class [System.Runtime]System.Func`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, object>> Program/'<>o__0'::'<>p__3'
IL_0198: ldloc.0
IL_0199: callvirt instance !2 class [System.Runtime]System.Func`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, object, object>::Invoke(!0, !1)
// (no C# code)
IL_019e: callvirt instance void class [System.Runtime]System.Action`3<class [System.Linq.Expressions]System.Runtime.CompilerServices.CallSite, class [System.Runtime]System.Type, object>::Invoke(!0, !1, !2)
// Console.WriteLine(num);
IL_01a3: nop
IL_01a4: ldloc.1
IL_01a5: call void [System.Console]System.Console::WriteLine(int32)
// }
IL_01aa: nop
IL_01ab: ret
} // end of method Program::'<Main>$'
The best way to know something is to measure, so I've wrote a benchmark. It will measure allocation to dynamic compared to explicit boxing.
using System.Dynamic;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
BenchmarkRunner.Run<MyBench>();
[MemoryDiagnoser]
public class MyBench
{
private dynamic _myObj = new ExpandoObject();
[Benchmark]
[Arguments(1)]
public void SetInt(int x)
{
_myObj.Int = x;
}
[Benchmark(Baseline = true)]
[Arguments(1)]
public object BoxInt(int x)
{
return x;
}
}
Which gives following result:
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19044.2728/21H2/November2021Update)
AMD Ryzen 5 3600X, 1 CPU, 12 logical and 6 physical cores
.NET SDK=7.0.100
[Host] : .NET 7.0.0 (7.0.22.51805), X64 RyuJIT AVX2
DefaultJob : .NET 7.0.0 (7.0.22.51805), X64 RyuJIT AVX2
Method | x | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio |
---|---|---|---|---|---|---|---|---|---|
SetInt | 1 | 100.150 ns | 0.2788 ns | 0.2608 ns | 35.81 | 0.15 | 0.0029 | 24 B | 1.00 |
BoxInt | 1 | 2.796 ns | 0.0105 ns | 0.0098 ns | 1.00 | 0.00 | 0.0029 | 24 B | 1.00 |
As you can see, both methods allocate same way - 24 bytes per method call. I bet int
gets boxed somewhere inside this machinery