Expression(Of Func(Of T)).Body.Member.Name bizarre "$vb$local_" added if used inside a Property Get Accessor
Asked Answered
J

1

1

I have observed the following bizarre behavior, and want to see if anyone already came across the same. In fact, I did quite a bit of searching, but have not bumped in anything related as yet.

It has somehow got quite conventional to supply a reference to a Property name from within a Class to a Method by means of a Lambda expression, instead the name String itself. So: RaisePropertyChanged("myProperty") gets RaisePropertyChanged(() => myProperty) in C# or RaisePropertyChanged(Function() myProperty) in VB .Net.

The called Method receives that Lambda expression in a System.Linq.Expressions.Expression<Func<T>> type in C# or Expression(Of Func(Of T)) in VB .Net.

In order the get the Property name in a String representation, the called Method retrieves the Expression(Of Func(Of T)).Body as a MemberExpression. Then accessing memberExprisson.Member.Name will usually get the proper Property name.

However, in VB .Net I have noticed the following bizarre behavior: When calling a method inside of Property Get stub supplying a Property by means such as (Function() myProperty) the memberExprisson.Member.Nameresults to: "$VB$Local_myProperty". So that's $VB$Local_ added in front of the Property name. Calling however from the Property Set stub worked as intended.

Whats more, when the result is OK, the memberExpression.Member'type is a System.Reflection.RuntimePropertyInfo. Whereas when the bizarre "$VB$Local_" is added, the memberExpression.Member results in a System.Reflection.RtFieldInfo type.

When examining the Expression Property of the above mentioned memberExpression thus: memberExpression.Expression, I find that the Type Property thereof will - on good behavior - have the proper Container Class name. On erroneous behavior however, that Type property will have a 'FullName' Property of something like "_Closure$__X" + the Container (Declaring) Class name. Further looking inside this Type property reveals that this FullName consist of a the Name of the Type itself which is "_Closure$__X" combined with the 'ReflectedType' which contains the proper Class name, resulting in this strange FullName. This "_Closure$__X" by the way, The 'X' represents a Number. It will be '1' inside the first Property Get stub and 2 for the second and so forth. So: "_Closure$__1", "_Closure$__2"...

Any comments?

EDIT:

For clarity here is a snapshot of the code:

Public Property RegisteredServer As Result
    Get
        Return GetProperty(Of Result)(Function() RegisteredServer)
    End Get
    Set(value As Result)
        SetProperty(Of Result)(Function() RegisteredServer, value)
    End Set
End Property

Public Property DefaultInstance As Result
    Get
        Return GetProperty(Function() DefaultInstance)
    End Get
    Set(value As Result)
        SetProperty(Function() DefaultInstance, value)
    End Set
End Property

GetPropertyAnd SetProperty are defined in the following code:

Private Function GetPropertyName(Of T)(propertyExpression As Expression(Of Func(Of T)))
    Dim memberExpr As MemberExpression = propertyExpression.Body
    If memberExpr Is Nothing Then
        Throw New ArgumentException("propertyExpression should represent access to a member")
    End If
    Dim memberName As String = memberExpr.Member.Name
    Return memberName
End Function

Shared Function CompareValues(Of T)(storage As T, value As T)
    Return Object.Equals(storage, value)
End Function

Protected Function SetProperty(Of T)(propertyExpression As Expression(Of Func(Of T)), value As T)
    Dim memberName As String = GetPropertyName(propertyExpression)
    Dim currentValue As T = Nothing
    _propertyBag.TryGetValue(memberName, currentValue)
    If CompareValues(currentValue, value) Then
        Return False
    End If
    _propertyBag(memberName) = value
    RaisePropertyChanged(memberName)
    Return True
End Function

Protected Function GetProperty(Of T)(propertyExpression As Expression(Of Func(Of T))) As T
    Dim memberName As String = GetPropertyName(propertyExpression)
    Dim value As T = Nothing
    _propertyBag.TryGetValue(memberName, value)
    Return value
End Function

Hope this helps.

Jemy answered 16/3, 2015 at 18:5 Comment(4)
pls show more code and if possible the place where you see the behaviour.. If possible pls post sample code of the behaviourMentality
The VB.NET compiler routinely generates fields and local variables with "bizarre" names when it generates code for non-trivial statements. They are intentionally bizarre, necessary so these names can never collide with fields and variables you declared yourself. If you want a complete explanation then post specific code that you are puzzled about.Cort
You could use a tool like Reflector to see what the compiler actually created for code and you'll find the very names that you mentionOutbreed
@Ric.Net What I'm trying here, is to get the pure Property name, without any leading added characters. And if not, at least, a safe way of knowing when to remove those characters. Can I assume that this added string will always be "$VB$Local_" or do I have to take measurements for all different kinds of literals added?Jemy
B
2

If you remember what Expressions are primarily used for, this makes sense: Expressions are generally used to compile into functions. An expression inside a Get Property that references itself would compile into an infinite loop. So instead of that lambda becoming a PropertyExpression, it becomes a FieldExpression on a closure:

GetProperty(Of Result)(Function() RegisteredServer) is the same GetProperty(Of Result)(Function() Me.RegisteredServer), so the compiler encloses the 'this' (Me) reference. A field expression around a closure can result in accessing a compiler-generated class, with weird names.

In your case, you don't really care about the 'this' reference, you just want a way to reference the Property in a strongly-typed way. You can do this by adding an explicit parameter so you're not enclosing anything:

    Public Function GetPropertyName(Of TClass, TProperty)(propertyExpression As Expression(Of Func(Of TClass, TProperty)))
        Dim memberExpr As MemberExpression = propertyExpression.Body
        If memberExpr Is Nothing Then
            Throw New ArgumentException("propertyExpression should represent access to a member")
        End If
        Dim memberName As String = memberExpr.Member.Name
        Return memberName
    End Function
    Protected Function GetProperty(Of TClass, TProperty)(propertyExpression As Expression(Of Func(Of TClass, TProperty))) As TProperty
        Dim memberName As String = GetPropertyName(propertyExpression)
        Dim value As TProperty = Nothing
        _propertyBag.TryGetValue(memberName, value)
        Return value
    End Function

... then GetProperty(Of Result)(Function() RegisteredServer) becomes GetProperty(Of YourClass, Result)(Function(c) c.RegisteredServer).


Edit: Upon further thought, you don't need the TClass type variable:

    Public Function GetPropertyName(Of T)(propertyExpression As Expression(Of Func(Of X, T)))
        Dim memberExpr As MemberExpression = propertyExpression.Body
        If memberExpr Is Nothing Then
            Throw New ArgumentException("propertyExpression should represent access to a member")
        End If
        Dim memberName As String = memberExpr.Member.Name
        Return memberName
    End Function


    Protected Function GetProperty(Of TProperty)(propertyExpression As Expression(Of Func(Of X, TProperty))) As TProperty
        Dim memberName As String = GetPropertyName(propertyExpression)
        Dim value As TProperty = Nothing
        _propertyBag.TryGetValue(memberName, value)
        Return value
    End Function

... where X is the name of your class. This means you can remove the type annotations from your GetProperty calls: So instead of GetProperty(Of Result)(Function() RegisteredServer) or GetProperty(Of YourClass, Result)(Function(c) c.RegisteredServer), you can now do: GetProperty(Function(c) c.RegisteredServer).

Boccie answered 19/3, 2015 at 16:6 Comment(3)
Excellent post! Shows a very deep understanding of that subject. Although, this makes using strongly-typed this way is almost useless as there's a lot more to code. What puzzles me, is: how comes that in C# we don't need all this diversion to get the proper property string representation.Jemy
It's not really a VB/C# thing. If you wrote it as an extension method, you could get it without all the type notations. I'll edit the answer accordingly.Boccie
Or to make it even more simpler, you can use the very original code. But instead of GetProperty(Function() MyProperty) you do GetProperty(Function() Me.MyProperty) and ignore the warning about the recursive call. Looks like the difference with C# is that in C# a Property is basically a Field with get and set functions attached to it. Whereas in VB it's a different breed. It's a Property. So when referring to Property in C# you're referring to a field. The compiler doesn't care about a get function attached to it.In VB you are referring to a Property and to get the Value it must invokeJemy

© 2022 - 2024 — McMap. All rights reserved.