9
votes

I was trying out clay in VB.Net but found that I could not get everything to work.

Here is Clay

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

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:

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>j__TPar' '<LatinName>i__Field'
.custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) 

And the public method Get_LatinName is this.

VB:

    .method public specialname instance !T0  get_LatinName() cil managed
{
  .custom instance void [mscorlib]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       11 (0xb)
  .maxstack  1
  .locals init (!T0 V_0)
  IL_0000:  ldarg.0
  IL_0001:  ldfld      !0 class VB$AnonymousType_0`1::$LatinName
  IL_0006:  stloc.0
  IL_0007:  br.s       IL_0009
  IL_0009:  ldloc.0
  IL_000a:  ret
} // end of method VB$AnonymousType_0`1::get_LatinName

C#:


    .method public hidebysig specialname instance !'j__TPar' 
        get_LatinName() cil managed
{
  // Code size       11 (0xb)
  .maxstack  1
  .locals init (!'j__TPar' V_0)
  IL_0000:  ldarg.0
  IL_0001:  ldfld      !0 class 'f__AnonymousType0`1'j__TPar'>::'i__Field'
  IL_0006:  stloc.0
  IL_0007:  br.s       IL_0009
  IL_0009:  ldloc.0
  IL_000a:  ret
} // end of method 'f__AnonymousType0`1'::get_LatinName

And these are the main methods:

VB:

    .method public static void  Main() cil managed
{
  .entrypoint
  .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       90 (0x5a)
  .maxstack  7
  .locals init ([0] object c,
           [1] object plant,
           [2] class VB$AnonymousType_0`1 VB$t_ref$S0,
           [3] object[] VB$t_array$S0)
  IL_0000:  nop
  IL_0001:  newobj     instance void [ClaySharp]ClaySharp.ClayFactory::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  ldnull
  IL_0009:  ldstr      "Plant"
  IL_000e:  ldc.i4.1
  IL_000f:  newarr     [mscorlib]System.Object
  IL_0014:  stloc.3
  IL_0015:  ldloc.3
  IL_0016:  ldc.i4.0
  IL_0017:  ldstr      "test"
  IL_001c:  newobj     instance void class VB$AnonymousType_0`1::.ctor(!0)
  IL_0021:  stelem.ref
  IL_0022:  nop
  IL_0023:  ldloc.3
  IL_0024:  ldnull
  IL_0025:  ldnull
  IL_0026:  ldnull
  IL_0027:  call       object [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.NewLateBinding::LateGet(object,
                                                                                                                    class [mscorlib]System.Type,
                                                                                                                    string,
                                                                                                                    object[],
                                                                                                                    string[],
                                                                                                                    class [mscorlib]System.Type[],
                                                                                                                    bool[])
  IL_002c:  call       object [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::GetObjectValue(object)
  IL_0031:  stloc.1
  IL_0032:  ldloc.1
  IL_0033:  ldnull
  IL_0034:  ldstr      "LatinName"
  IL_0039:  ldc.i4.0
  IL_003a:  newarr     [mscorlib]System.Object
  IL_003f:  ldnull
  IL_0040:  ldnull
  IL_0041:  ldnull
  IL_0042:  call       object [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.NewLateBinding::LateGet(object,
                                                                                                                    class [mscorlib]System.Type,
                                                                                                                    string,
                                                                                                                    object[],
                                                                                                                    string[],
                                                                                                                    class [mscorlib]System.Type[],
                                                                                                                    bool[])
  IL_0047:  call       object [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::GetObjectValue(object)
  IL_004c:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_0051:  nop
  IL_0052:  call       string [mscorlib]System.Console::ReadLine()
  IL_0057:  pop
  IL_0058:  nop
  IL_0059:  ret
} // end of method Module1::Main

C#:

    .method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       299 (0x12b)
  .maxstack  10
  .locals init ([0] object c,
           [1] object plant,
           [2] class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo[] CS$0$0000)
  IL_0000:  nop
  IL_0001:  newobj     instance void [ClaySharp]ClaySharp.ClayFactory::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1f__AnonymousType0`1',object>> ConsoleApplication2.Program/'o__SiteContainer0'::'p__Site1'
  IL_000c:  brtrue.s   IL_004c
  IL_000e:  ldc.i4.0
  IL_000f:  ldstr      "Plant"
  IL_0014:  ldnull
  IL_0015:  ldtoken    ConsoleApplication2.Program
  IL_001a:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
  IL_001f:  ldc.i4.2
  IL_0020:  newarr     [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo
  IL_0025:  stloc.2
  IL_0026:  ldloc.2
  IL_0027:  ldc.i4.0
  IL_0028:  ldc.i4.0
  IL_0029:  ldnull
  IL_002a:  call       class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags,
                                                                                                                                                                             string)
  IL_002f:  stelem.ref
  IL_0030:  ldloc.2
  IL_0031:  ldc.i4.1
  IL_0032:  ldc.i4.1
  IL_0033:  ldnull
  IL_0034:  call       class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags,
                                                                                                                                                                             string)
  IL_0039:  stelem.ref
  IL_003a:  ldloc.2
  IL_003b:  call       class [System.Core]System.Runtime.CompilerServices.CallSiteBinder [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.Binder::InvokeMember(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags,
                                                                                                                                                               string,
                                                                                                                                                               class [mscorlib]System.Collections.Generic.IEnumerable`1,
                                                                                                                                                               class [mscorlib]System.Type,
                                                                                                                                                               class [mscorlib]System.Collections.Generic.IEnumerable`1)
  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)
  IL_0045:  stsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1f__AnonymousType0`1',object>> ConsoleApplication2.Program/'o__SiteContainer0'::'p__Site1'
  IL_004a:  br.s       IL_004c
  IL_004c:  ldsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1f__AnonymousType0`1',object>> ConsoleApplication2.Program/'o__SiteContainer0'::'p__Site1'
  IL_0051:  ldfld      !0 class [System.Core]System.Runtime.CompilerServices.CallSite`1f__AnonymousType0`1',object>>::Target
  IL_0056:  ldsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1f__AnonymousType0`1',object>> ConsoleApplication2.Program/'o__SiteContainer0'::'p__Site1'
  IL_005b:  ldloc.0
  IL_005c:  ldstr      "test"
  IL_0061:  newobj     instance void class 'f__AnonymousType0`1'::.ctor(!0)
  IL_0066:  callvirt   instance !3 class [mscorlib]System.Func`4f__AnonymousType0`1',object>::Invoke(!0,
                                                                                                                                                                                          !1,
                                                                                                                                                                                          !2)
  IL_006b:  stloc.1
  IL_006c:  ldsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1> ConsoleApplication2.Program/'o__SiteContainer0'::'p__Site2'
  IL_0071:  brtrue.s   IL_00b6
  IL_0073:  ldc.i4     0x100
  IL_0078:  ldstr      "WriteLine"
  IL_007d:  ldnull
  IL_007e:  ldtoken    ConsoleApplication2.Program
  IL_0083:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
  IL_0088:  ldc.i4.2
  IL_0089:  newarr     [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo
  IL_008e:  stloc.2
  IL_008f:  ldloc.2
  IL_0090:  ldc.i4.0
  IL_0091:  ldc.i4.s   33
  IL_0093:  ldnull
  IL_0094:  call       class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags,
                                                                                                                                                                             string)
  IL_0099:  stelem.ref
  IL_009a:  ldloc.2
  IL_009b:  ldc.i4.1
  IL_009c:  ldc.i4.0
  IL_009d:  ldnull
  IL_009e:  call       class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags,
                                                                                                                                                                             string)
  IL_00a3:  stelem.ref
  IL_00a4:  ldloc.2
  IL_00a5:  call       class [System.Core]System.Runtime.CompilerServices.CallSiteBinder [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.Binder::InvokeMember(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags,
                                                                                                                                                               string,
                                                                                                                                                               class [mscorlib]System.Collections.Generic.IEnumerable`1,
                                                                                                                                                               class [mscorlib]System.Type,
                                                                                                                                                               class [mscorlib]System.Collections.Generic.IEnumerable`1)
  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)
  IL_00af:  stsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1> ConsoleApplication2.Program/'o__SiteContainer0'::'p__Site2'
  IL_00b4:  br.s       IL_00b6
  IL_00b6:  ldsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1> ConsoleApplication2.Program/'o__SiteContainer0'::'p__Site2'
  IL_00bb:  ldfld      !0 class [System.Core]System.Runtime.CompilerServices.CallSite`1>::Target
  IL_00c0:  ldsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1> ConsoleApplication2.Program/'o__SiteContainer0'::'p__Site2'
  IL_00c5:  ldtoken    [mscorlib]System.Console
  IL_00ca:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
  IL_00cf:  ldsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1> ConsoleApplication2.Program/'o__SiteContainer0'::'p__Site3'
  IL_00d4:  brtrue.s   IL_0109
  IL_00d6:  ldc.i4.0
  IL_00d7:  ldstr      "LatinName"
  IL_00dc:  ldtoken    ConsoleApplication2.Program
  IL_00e1:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
  IL_00e6:  ldc.i4.1
  IL_00e7:  newarr     [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo
  IL_00ec:  stloc.2
  IL_00ed:  ldloc.2
  IL_00ee:  ldc.i4.0
  IL_00ef:  ldc.i4.0
  IL_00f0:  ldnull
  IL_00f1:  call       class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags,
                                                                                                                                                                             string)
  IL_00f6:  stelem.ref
  IL_00f7:  ldloc.2
  IL_00f8:  call       class [System.Core]System.Runtime.CompilerServices.CallSiteBinder [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.Binder::GetMember(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags,
                                                                                                                                                            string,
                                                                                                                                                            class [mscorlib]System.Type,
                                                                                                                                                            class [mscorlib]System.Collections.Generic.IEnumerable`1)
  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)
  IL_0102:  stsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1> ConsoleApplication2.Program/'o__SiteContainer0'::'p__Site3'
  IL_0107:  br.s       IL_0109
  IL_0109:  ldsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1> ConsoleApplication2.Program/'o__SiteContainer0'::'p__Site3'
  IL_010e:  ldfld      !0 class [System.Core]System.Runtime.CompilerServices.CallSite`1>::Target
  IL_0113:  ldsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1> ConsoleApplication2.Program/'o__SiteContainer0'::'p__Site3'
  IL_0118:  ldloc.1
  IL_0119:  callvirt   instance !2 class [mscorlib]System.Func`3::Invoke(!0,
                                                                                                                                                    !1)
  IL_011e:  callvirt   instance void class [mscorlib]System.Action`3::Invoke(!0,
                                                                                                                                                                             !1,
                                                                                                                                                                             !2)
  IL_0123:  nop
  IL_0124:  call       string [mscorlib]System.Console::ReadLine()
  IL_0129:  pop
  IL_012a:  ret
} // end of method Program::Main

The C# and VB version are nothing alike. Seems like the C# version does a lot more.

1
That's a strange error considering there's no lambda in there.Etienne de Martel
It bombs when compiling the expression tree. To many undebuggable tricks in this code.Hans Passant
since your blog has the answer i think you should post it here.jbtule
One reason why it looks like the VB code is doing less is because it is: Because Console.WriteLine has an Object overload VB just uses it, whereas C# does late binding on that call too.Mark Hurd

1 Answers

2
votes

I don't know anything about Clay, but there is one potentially important difference between C# anonymous types and VB ones: C# anonymous types always have read-only properties, whereas by default, VB ones are mutable.

Only the read-only properties are used in equality and hash code generation in VB. These can be specified with the Key keyword. So to be closer to your C# code, the VB should be:

Dim c As Object = New ClayFactory
Dim plant = c.Plant(New With { Key .LatinName = "test"})
Console.WriteLine(plant.LatinName)
Console.ReadLine()

Give that a try and see whether it helps :)