Introduction
Yesterday I made a post about using [Clay with VB.Net][1]. 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][2]. But no solution yet.
The problem
This works in C#
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:
> <code class="codespan">.field private initonly !'<latinname>j__TPar' '<latinname>i__Field'<br />
.custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) </latinname></latinname></code>
And the public method Get_LatinName is this.
VB:
> <code class="codespan">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</code>
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 (!'j__TPar' V_0)<br />
IL_0000: ldarg.0<br />
IL_0001: ldfld !0 class 'f__AnonymousType0`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</code>
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</code>
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/'o__SiteContainer0'::'p__Site1'<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.IEnumerable`1,<br />
class [mscorlib]System.Type,<br />
class [mscorlib]System.Collections.Generic.IEnumerable`1)<br />
IL_0040: call class [System.Core]System.Runtime.CompilerServices.CallSite`1 class [System.Core]System.Runtime.CompilerServices.CallSite`1f__AnonymousType0`1',object>>::Create(class [System.Core]System.Runtime.CompilerServices.CallSiteBinder)<br />
IL_0045: stsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1f__AnonymousType0`1',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`1f__AnonymousType0`1',object>> ConsoleApplication2.Program/'o__SiteContainer0'::'p__Site1'<br />
IL_0051: ldfld !0 class [System.Core]System.Runtime.CompilerServices.CallSite`1f__AnonymousType0`1',object>>::Target<br />
IL_0056: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1f__AnonymousType0`1',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.Func`4f__AnonymousType0`1',object>::Invoke(!0,<br />
!1,<br />
!2)<br />
IL_006b: stloc.1<br />
IL_006c: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1> 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.IEnumerable`1)<br />
IL_00aa: call class [System.Core]System.Runtime.CompilerServices.CallSite`1 class [System.Core]System.Runtime.CompilerServices.CallSite`1>::Create(class [System.Core]System.Runtime.CompilerServices.CallSiteBinder)<br />
IL_00af: stsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1> ConsoleApplication2.Program/'o__SiteContainer0'::'p__Site2'<br />
IL_00b4: br.s IL_00b6<br />
IL_00b6: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1> 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.CallSite`1> 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/'o__SiteContainer0'::'p__Site3'<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.IEnumerable`1)<br />
IL_00fd: call class [System.Core]System.Runtime.CompilerServices.CallSite`1 class [System.Core]System.Runtime.CompilerServices.CallSite`1>::Create(class [System.Core]System.Runtime.CompilerServices.CallSiteBinder)<br />
IL_0102: stsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1> ConsoleApplication2.Program/'o__SiteContainer0'::'p__Site3'<br />
IL_0107: br.s IL_0109<br />
IL_0109: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1> 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.CallSite`1> 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</code>
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][3] 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.
[1]: /index.php/DesktopDev/MSTech/using-clay-in-vb-net
[2]: http://stackoverflow.com/questions/5922964/why-do-my-anonymous-types-not-work-in-clay-when-using-vb-net-but-do-work-in-c
[3]: http://msdn.microsoft.com/en-us/library/dd466445.aspx