Linq.Expression GetValue in VB?
Asked Answered
B

1

4

Question:

I have this C# program, that gets the value of field tablename of mytable.
And it works fine.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;


namespace AttachObjectsCS
{


    static class Program
    {


        public class cmytable
        {
            public string tablename = "test";
        }


        // https://mcmap.net/q/268220/-access-the-value-of-a-member-expression
        private static object GetValue(System.Linq.Expressions.MemberExpression member)
        {
            System.Linq.Expressions.Expression objectMember = System.Linq.Expressions.Expression.Convert(member, typeof(object));
            System.Linq.Expressions.Expression<Func<object>> getterLambda = System.Linq.Expressions.Expression.Lambda<Func<object>>(objectMember);

            var getter = getterLambda.Compile();

            return getter();
        }


        public static void AddField<T>(System.Linq.Expressions.Expression<Func<T>> expr, string alias)
        {
            var body = ((System.Linq.Expressions.MemberExpression)expr.Body);
            Console.WriteLine("Name is: {0}", body.Member.Name);


            object obj = GetValue(body);


            Console.WriteLine("Value is: {0}", obj);
        }



        /// <summary>
        /// Der Haupteinstiegspunkt für die Anwendung.
        /// </summary>
        [STAThread]
        static void Main()
        {

            if (false)
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new Form1());
            }


            cmytable mytable = new cmytable();

            AddField(() => mytable.tablename, "te");


            Console.WriteLine(Environment.NewLine);
            Console.WriteLine(" --- Press any key to continue --- ");
            Console.ReadKey();
        } // End Sub Main 


    } // End Class Program


} // End Namespace AttachObjectsCS

Now I need the same functionality in VB.NET, but it fails.
Now since I'm aware that VB.NET and C# don't always use the same linq expression, I'm not surprised that I run into a problem here.

Module Program




    Public Class cmytable
        Public tablename As String = "test"
    End Class


    Public Sub AddField(Of T)(expr As System.Linq.Expressions.Expression(Of Func(Of T)))
        Dim body = DirectCast(expr.Body, System.Linq.Expressions.MemberExpression)
        Dim strName As String = body.Member.Name
        Console.WriteLine("Name is: {0}", strName)


        Dim obj As Object = GetValue(body)


        Console.WriteLine("Value is: {0}", obj)
    End Sub


    ' https://mcmap.net/q/268220/-access-the-value-of-a-member-expression '
    Private Function GetValue(member As System.Linq.Expressions.MemberExpression) As Object
        Dim objectMember As System.Linq.Expressions.Expression = System.Linq.Expressions.Expression.Convert(member, GetType(Object))
        Dim getterLambda As System.Linq.Expressions.Expression(Of Func(Of Object)) = System.Linq.Expressions.Expression.Lambda(Of Func(Of Object))(objectMember)


        Dim getter = getterLambda.Compile()

        Return getter()
    End Function



    Public Sub Main()

        Dim mytable As New cmytable

        AddField(Function() mytable.tablename)
    End Sub


End Module ' Program

The problem is in GetValue, and the problem is that I don't seem to have the proper objectMember.

Here the debug output for the expressions:

CS objectMember = {Convert(value(AttachObjectsCS.Program+<>c__DisplayClass0).mytable.tablename)}
VB objectMember = {Convert(value(AttachObjects.Program+_Closure$__1).$VB$Local_mytable.tablename)}

CS getterLambda = {() => Convert(value(AttachObjectsCS.Program+<>c__DisplayClass0).mytable.tablename)}
VB getterLambda = {() => Convert(value(AttachObjects.Program+_Closure$__1).$VB$Local_mytable.tablename)}

My guess would be that the problem is the $VB$Local_ in $VB$Local_mytable.tablename

Now I'm wondering what I have to change for this to work in VB.NET.
Anybody knows / has a clue ?

Edit:
Ah, the problem seems to be caused by "Option Infer Off" in the project wide settings.
So the the question alters and becomes: How to do this with "Option Infer Off" ?

Beaman answered 5/6, 2013 at 12:7 Comment(7)
Your VB code works fine for me.Gallican
Both run fine for me also (VS 2008, x64)Mollie
@svick: Ah, very interesting. That leaves one possible reason. Tested hypothesis & found right --> The problem is caused by "Option Infer Off" in the project-wide settings. Now the new question is what do i need to do to get it working with infer = off.Beaman
Leave Option Infer on? Its nice to have the compiler do the work for you :)Mollie
@Pondidum: No, because it's not so nice to meet some other errors at runtime instead of compile-time. It's already bad enough I have to disable option strict because I (have to) use oledb-com-objects (which requires late binding --> which in turn requires option strict off).Beaman
@Quandary I don;t know much about VB, but I don't see why would Option Infer On do that. And you should probably set Option Strict On globally and turn it off only in the files where you actually need it.Gallican
@svick: You're right. Good advice. I have done so.Beaman
G
2

Your problem has nothing to do with expression trees (which would be easier to spot if you bothered to include the error you're getting).

You have Option Infer Off and Option Strict Off and the following code:

Dim getter = getterLambda.Compile()

Return getter()

which throws MissingMemberException:

No default member found for type 'Func(Of Object)'.

The problem is that () is here interpreted as invoking the default member and not invoking a delegate, because getter is typed as Object. To work around that, you can specify the type of the variable:

Dim getter As Func(Of Object) = getterLambda.Compile()

Return getter()

or you can use more verbose syntax for invoking a delegate:

Dim getter = getterLambda.Compile()

Return getter.Invoke()
Gallican answered 5/6, 2013 at 14:6 Comment(1)
Oh crap, this just demonstrates how wrong it is to use "Option Strict Off" (and "Option Infer On" with var in C#). Thanks !Beaman

© 2022 - 2024 — McMap. All rights reserved.