Introduction
Yesterday I made a post about using Clay with VB.Net. Clay works but not all functionality we have in C# work in VB.Net, which is mildly frustrating. To see if I could find a solution I posted a question on StackOverflow. But no solution yet.
The problem
This works in C#
text
dynamic c = new ClayFactory();
var plant = c.Plant(new {LatinName = "test"});
Console.WriteLine(plant.LatinName);
Console.ReadLine();
but this does not work in VB.Net
vbnet
Dim c As Object = New ClayFactory
Dim plant = c.Plant(New With {.LatinName = "test"})
Console.WriteLine(plant.LatinName)
Console.ReadLine()
I get this error message in VB.Net:
Cannot close over byref parameter
‘$arg1’ referenced in lambda ”
I’m not 100% sure how to solve this if I can even solve it. I’m guessing the VB.Net implementation of anonymous types is slightly different.
I get the error on this line:
vbnet
Dim plant = c.Plant(New With {.LatinName = "test"})
I would appreciate if someone could explain this to me.
The IL seems to be quit different.
For VB the private field Latinname is this:
.field private initonly !T0 $LatinName
For C# it is:
.field private initonly !’<latinname>jTPar’ ‘<latinname>iField’<br /> .custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) </latinname></latinname>
And the public method Get_LatinName is this.
VB:
ce !T0 get_LatinName() cil managed<br /> {<br /> .custom instance void [mscorlib]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 )<br /> // Code size 11 (0xb)<br /> .maxstack 1<br /> .locals init (!T0 V_0)<br /> IL_0000: ldarg.0<br /> IL_0001: ldfld !0 class VB$AnonymousType_0
1::$LatinName<br /> IL_0006: stloc.0<br /> IL_0007: br.s IL_0009<br /> IL_0009: ldloc.0<br /> IL_000a: ret<br /> } // end of method VB$AnonymousType_0
1::get_LatinName
C#:
<code class="codespan">.method public hidebysig specialname instance !'j__TPar'<br /> get_LatinName() cil managed<br />
{<br /> // Code size 11 (0xb)<br /> .maxstack 1<br /> .locals init (!‘jTPar’ V_0)<br /> IL_0000: ldarg.0<br /> IL_0001: ldfld !0 class ‘fAnonymousType0
1'j__TPar'>::'i__Field'<br /> IL_0006: stloc.0<br /> IL_0007: br.s IL_0009<br /> IL_0009: ldloc.0<br /> IL_000a: ret<br /> } // end of method 'f__AnonymousType0
1’::get_LatinName
And these are the main methods:
VB:
<code class="codespan">.method public static void Main() cil managed<br />
{<br /> .entrypoint<br /> .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 )<br /> // Code size 90 (0x5a)<br /> .maxstack 7<br /> .locals init ([0] object c,<br /> 1 object plant,<br /> 2 class VB$AnonymousType_0
1 VB$t_ref$S0,<br /> [3] object[] VB$t_array$S0)<br /> IL_0000: nop<br /> IL_0001: newobj instance void [ClaySharp]ClaySharp.ClayFactory::.ctor()<br /> IL_0006: stloc.0<br /> IL_0007: ldloc.0<br /> IL_0008: ldnull<br /> IL_0009: ldstr "Plant"<br /> IL_000e: ldc.i4.1<br /> IL_000f: newarr [mscorlib]System.Object<br /> IL_0014: stloc.3<br /> IL_0015: ldloc.3<br /> IL_0016: ldc.i4.0<br /> IL_0017: ldstr "test"<br /> IL_001c: newobj instance void class VB$AnonymousType_0
1::.ctor(!0)<br /> IL_0021: stelem.ref<br /> IL_0022: nop<br /> IL_0023: ldloc.3<br /> IL_0024: ldnull<br /> IL_0025: ldnull<br /> IL_0026: ldnull<br /> IL_0027: call object [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.NewLateBinding::LateGet(object,<br /> class [mscorlib]System.Type,<br /> string,<br /> object[],<br /> string[],<br /> class [mscorlib]System.Type[],<br /> bool[])<br /> IL_002c: call object [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::GetObjectValue(object)<br /> IL_0031: stloc.1<br /> IL_0032: ldloc.1<br /> IL_0033: ldnull<br /> IL_0034: ldstr “LatinName”<br /> IL_0039: ldc.i4.0<br /> IL_003a: newarr [mscorlib]System.Object<br /> IL_003f: ldnull<br /> IL_0040: ldnull<br /> IL_0041: ldnull<br /> IL_0042: call object [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.NewLateBinding::LateGet(object,<br /> class [mscorlib]System.Type,<br /> string,<br /> object[],<br /> string[],<br /> class [mscorlib]System.Type[],<br /> bool[])<br /> IL_0047: call object [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::GetObjectValue(object)<br /> IL_004c: call void [mscorlib]System.Console::WriteLine(object)<br /> IL_0051: nop<br /> IL_0052: call string [mscorlib]System.Console::ReadLine()<br /> IL_0057: pop<br /> IL_0058: nop<br /> IL_0059: ret<br /> } // end of method Module1::Main
C#:
<code class="codespan">.method private hidebysig static void Main(string[] args) cil managed<br />
{<br /> .entrypoint<br /> // Code size 299 (0x12b)<br /> .maxstack 10<br /> .locals init ([0] object c,<br /> 1 object plant,<br /> 2 class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo[] CS$0$0000)<br /> IL_0000: nop<br /> IL_0001: newobj instance void [ClaySharp]ClaySharp.ClayFactory::.ctor()<br /> IL_0006: stloc.0<br /> IL_0007: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite
1f__AnonymousType0
1’,object>> ConsoleApplication2.Program/‘oSiteContainer0’::‘pSite1’<br /> IL_000c: brtrue.s IL_004c<br /> IL_000e: ldc.i4.0<br /> IL_000f: ldstr “Plant”<br /> IL_0014: ldnull<br /> IL_0015: ldtoken ConsoleApplication2.Program<br /> IL_001a: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)<br /> IL_001f: ldc.i4.2<br /> IL_0020: newarr [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo<br /> IL_0025: stloc.2<br /> IL_0026: ldloc.2<br /> IL_0027: ldc.i4.0<br /> IL_0028: ldc.i4.0<br /> IL_0029: ldnull<br /> IL_002a: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags,<br /> string)<br /> IL_002f: stelem.ref<br /> IL_0030: ldloc.2<br /> IL_0031: ldc.i4.1<br /> IL_0032: ldc.i4.1<br /> IL_0033: ldnull<br /> IL_0034: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags,<br /> string)<br /> IL_0039: stelem.ref<br /> IL_003a: ldloc.2<br /> IL_003b: call class [System.Core]System.Runtime.CompilerServices.CallSiteBinder [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.Binder::InvokeMember(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags,<br /> string,<br /> class [mscorlib]System.Collections.Generic.IEnumerable1,<br /> class [mscorlib]System.Type,<br /> class [mscorlib]System.Collections.Generic.IEnumerable
1)<br /> IL_0040: call class [System.Core]System.Runtime.CompilerServices.CallSite1 class [System.Core]System.Runtime.CompilerServices.CallSite
1fAnonymousType01',object>>::Create(class [System.Core]System.Runtime.CompilerServices.CallSiteBinder)<br /> IL_0045: stsfld class [System.Core]System.Runtime.CompilerServices.CallSite
1fAnonymousType01',object>> ConsoleApplication2.Program/'o__SiteContainer0'::'p__Site1'<br /> IL_004a: br.s IL_004c<br /> IL_004c: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite
1fAnonymousType01',object>> ConsoleApplication2.Program/'o__SiteContainer0'::'p__Site1'<br /> IL_0051: ldfld !0 class [System.Core]System.Runtime.CompilerServices.CallSite
1fAnonymousType01',object>>::Target<br /> IL_0056: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite
1fAnonymousType01',object>> ConsoleApplication2.Program/'o__SiteContainer0'::'p__Site1'<br /> IL_005b: ldloc.0<br /> IL_005c: ldstr "test"<br /> IL_0061: newobj instance void class 'f__AnonymousType0
1’::.ctor(!0)<br /> IL_0066: callvirt instance !3 class [mscorlib]System.Func4f__AnonymousType0
1’,object>::Invoke(!0,<br /> !1,<br /> !2)<br /> IL_006b: stloc.1<br /> IL_006c: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite1> ConsoleApplication2.Program/'o__SiteContainer0'::'p__Site2'<br /> IL_0071: brtrue.s IL_00b6<br /> IL_0073: ldc.i4 0x100<br /> IL_0078: ldstr "WriteLine"<br /> IL_007d: ldnull<br /> IL_007e: ldtoken ConsoleApplication2.Program<br /> IL_0083: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)<br /> IL_0088: ldc.i4.2<br /> IL_0089: newarr [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo<br /> IL_008e: stloc.2<br /> IL_008f: ldloc.2<br /> IL_0090: ldc.i4.0<br /> IL_0091: ldc.i4.s 33<br /> IL_0093: ldnull<br /> IL_0094: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags,<br /> string)<br /> IL_0099: stelem.ref<br /> IL_009a: ldloc.2<br /> IL_009b: ldc.i4.1<br /> IL_009c: ldc.i4.0<br /> IL_009d: ldnull<br /> IL_009e: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags,<br /> string)<br /> IL_00a3: stelem.ref<br /> IL_00a4: ldloc.2<br /> IL_00a5: call class [System.Core]System.Runtime.CompilerServices.CallSiteBinder [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.Binder::InvokeMember(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags,<br /> string,<br /> class [mscorlib]System.Collections.Generic.IEnumerable
1,<br /> class [mscorlib]System.Type,<br /> class [mscorlib]System.Collections.Generic.IEnumerable1)<br /> IL_00aa: call class [System.Core]System.Runtime.CompilerServices.CallSite
1 class [System.Core]System.Runtime.CompilerServices.CallSite1>::Create(class [System.Core]System.Runtime.CompilerServices.CallSiteBinder)<br /> IL_00af: stsfld class [System.Core]System.Runtime.CompilerServices.CallSite
1> ConsoleApplication2.Program/‘oSiteContainer0’::‘pSite2’<br /> IL_00b4: br.s IL_00b6<br /> IL_00b6: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite1> ConsoleApplication2.Program/'o__SiteContainer0'::'p__Site2'<br /> IL_00bb: ldfld !0 class [System.Core]System.Runtime.CompilerServices.CallSite
1>::Target<br /> IL_00c0: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite1> ConsoleApplication2.Program/'o__SiteContainer0'::'p__Site2'<br /> IL_00c5: ldtoken [mscorlib]System.Console<br /> IL_00ca: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)<br /> IL_00cf: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite
1> ConsoleApplication2.Program/‘oSiteContainer0’::‘pSite3’<br /> IL_00d4: brtrue.s IL_0109<br /> IL_00d6: ldc.i4.0<br /> IL_00d7: ldstr “LatinName”<br /> IL_00dc: ldtoken ConsoleApplication2.Program<br /> IL_00e1: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)<br /> IL_00e6: ldc.i4.1<br /> IL_00e7: newarr [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo<br /> IL_00ec: stloc.2<br /> IL_00ed: ldloc.2<br /> IL_00ee: ldc.i4.0<br /> IL_00ef: ldc.i4.0<br /> IL_00f0: ldnull<br /> IL_00f1: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags,<br /> string)<br /> IL_00f6: stelem.ref<br /> IL_00f7: ldloc.2<br /> IL_00f8: call class [System.Core]System.Runtime.CompilerServices.CallSiteBinder [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.Binder::GetMember(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags,<br /> string,<br /> class [mscorlib]System.Type,<br /> class [mscorlib]System.Collections.Generic.IEnumerable1)<br /> IL_00fd: call class [System.Core]System.Runtime.CompilerServices.CallSite
1 class [System.Core]System.Runtime.CompilerServices.CallSite1>::Create(class [System.Core]System.Runtime.CompilerServices.CallSiteBinder)<br /> IL_0102: stsfld class [System.Core]System.Runtime.CompilerServices.CallSite
1> ConsoleApplication2.Program/‘oSiteContainer0’::‘p__Site3’<br /> IL_0107: br.s IL_0109<br /> IL_0109: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite1> ConsoleApplication2.Program/'o__SiteContainer0'::'p__Site3'<br /> IL_010e: ldfld !0 class [System.Core]System.Runtime.CompilerServices.CallSite
1>::Target<br /> IL_0113: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite1> ConsoleApplication2.Program/'o__SiteContainer0'::'p__Site3'<br /> IL_0118: ldloc.1<br /> IL_0119: callvirt instance !2 class [mscorlib]System.Func
3::Invoke(!0,<br /> !1)<br /> IL_011e: callvirt instance void class [mscorlib]System.Action`3::Invoke(!0,<br /> !1,<br /> !2)<br /> IL_0123: nop<br /> IL_0124: call string [mscorlib]System.Console::ReadLine()<br /> IL_0129: pop<br /> IL_012a: ret<br /> } // end of method Program::Main
The C# and VB version are nothing alike. Seems like the C# version does a lot more. So there goes the theory C# and VB.Net compile to the same IL.
I also tried debugging Clay to see which line was causing the problem on their side.
The problem is that it is building a whole expressiontree and that it is difficult to say where it actually fails.
```csharp public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args) { Logger.Log(LogLevel.Debug, null, “BindInvokeMember”);
var argValues = Expression.NewArrayInit(typeof(object), args.Select(x => Expression.Convert(x.Expression, typeof(Object))));
var argNames = Expression.Constant(binder.CallInfo.ArgumentNames, typeof(IEnumerable<string>));
var argNamedEnumerable = Expression.Call(typeof(Arguments).GetMethod("From"), argValues, argNames);
var binderDefault = binder.FallbackInvokeMember(this, args);
var missingLambda = Expression.Lambda(Expression.Call(
GetClayBehavior(),
IClayBehavior_InvokeMemberMissing,
Expression.Lambda(binderDefault.Expression),
GetLimitedSelf(),
Expression.Constant(binder.Name, typeof(string)),
argNamedEnumerable));
var call = Expression.Call(
GetClayBehavior(),
IClayBehavior_InvokeMember,
missingLambda,
GetLimitedSelf(),
Expression.Constant(binder.Name, typeof(string)),
argNamedEnumerable);
var dynamicSuggestion = new DynamicMetaObject(
call, BindingRestrictions.GetTypeRestriction(Expression, LimitType).Merge(binderDefault.Restrictions));
return binder.FallbackInvokeMember(this, args, dynamicSuggestion);
}```
I think it bombs out on the binder.FallBackInvokeMember. And that just invokes the expressiontree.
But nothing on MSDN seems to suggest this is not compatible with VB.Net or that they have to be carefull about something.
Conclusion
For the moment I am at a lose if you know someone that can help then please do so.