Login or Sign Up to become a member!
LessThanDot Sit Logo

LessThanDot

Desktop Developer

Less Than Dot is a community of passionate IT professionals and enthusiasts dedicated to sharing technical knowledge, experience, and assistance. Inside you will find reference materials, interesting technical discussions, and expert tips and commentary. Once you register for an account you will have immediate access to the forums and all past articles and commentaries.

LTD Social Sitings

Lessthandot twitter Lessthandot Linkedin Lessthandot friendfeed Lessthandot facebook Lessthandot rss

Note: Watch for social icons on posts by your favorite authors to follow their postings on these and other social sites.

Your profile

    Search

    XML Feeds

    Google Ads

    « The mystery of Clay and VB.Net a better explanationUsing clay in VB.Net or how to be dynamic »
    comments

    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#

    1. dynamic c = new ClayFactory();
    2.         var plant = c.Plant(new {LatinName = "test"});
    3.         Console.WriteLine(plant.LatinName);
    4.         Console.ReadLine();

    but this does not work in VB.Net

    1. Dim c As Object = New ClayFactory
    2.         Dim plant = c.Plant(New With {.LatinName = "test"})
    3.         Console.WriteLine(plant.LatinName)
    4.         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:

    1. 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 ) </latinname></latinname>

    And the public method Get_LatinName is this.

    VB:

    ce !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. 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.

    1. public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args) {
    2.             Logger.Log(LogLevel.Debug, null, "BindInvokeMember");
    3.  
    4.             var argValues = Expression.NewArrayInit(typeof(object), args.Select(x => Expression.Convert(x.Expression, typeof(Object))));
    5.             var argNames = Expression.Constant(binder.CallInfo.ArgumentNames, typeof(IEnumerable<string>));
    6.             var argNamedEnumerable = Expression.Call(typeof(Arguments).GetMethod("From"), argValues, argNames);
    7.  
    8.             var binderDefault = binder.FallbackInvokeMember(this, args);
    9.  
    10.             var missingLambda = Expression.Lambda(Expression.Call(
    11.                 GetClayBehavior(),
    12.                 IClayBehavior_InvokeMemberMissing,
    13.                 Expression.Lambda(binderDefault.Expression),
    14.                 GetLimitedSelf(),
    15.                 Expression.Constant(binder.Name, typeof(string)),
    16.                 argNamedEnumerable));
    17.  
    18.             var call = Expression.Call(
    19.                 GetClayBehavior(),
    20.                 IClayBehavior_InvokeMember,
    21.                 missingLambda,
    22.                 GetLimitedSelf(),
    23.                 Expression.Constant(binder.Name, typeof(string)),
    24.                 argNamedEnumerable);
    25.  
    26.             var dynamicSuggestion = new DynamicMetaObject(
    27.                 call, BindingRestrictions.GetTypeRestriction(Expression, LimitType).Merge(binderDefault.Restrictions));
    28.  
    29.             return binder.FallbackInvokeMember(this, args, dynamicSuggestion);
    30.         }

    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.

    About the Author

    User bio imageChris is awesome.
    Social SitingsTwitterHomePageLTD RSS Feed
    c#, clay, vb.net
    InstapaperVote on HN

    No feedback yet

    Leave a comment


    Your email address will not be revealed on this site.

    To mislead the spambots.

    Your URL will be displayed.
    (Line breaks become <br />)
    (Name, email & website)
    (Allow users to contact you through a message form (your email will not be revealed.)