Expression(Of Func(Of T)).Body.Member.Name 如果在 属性 Get 访问器中使用,则添加奇怪的“$vb$local_”

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

我观察到以下奇怪的行为,想看看是否有人遇到过同样的情况。事实上,我做了很多搜索,但还没有碰到任何相关的东西。

通过 Lambda 表达式从 Class 中向方法提供对 属性 名称的引用,而不是名称 String 本身,这在某种程度上已经变得非常传统。所以:RaisePropertyChanged("myProperty") 在 C# 中得到 RaisePropertyChanged(() => myProperty) 或在 VB .Net 中得到 RaisePropertyChanged(Function() myProperty)

被调用方法在 C# 中的 System.Linq.Expressions.Expression<Func<T>> 类型或 VB .Net 中的 Expression(Of Func(Of T)) 中接收 Lambda 表达式。

为了在字符串表示中获取 属性 名称,被调用的方法将 Expression(Of Func(Of T)).Body 作为 MemberExpression 检索。然后访问 memberExprisson.Member.Name 通常会得到正确的 属性 名称。

但是,在 VB .Net 中,我注意到以下奇怪的行为:在 属性 中调用方法时,通过 [=23] 等方式获取提供 属性 的存根=] memberExprisson.Member.Name 结果为:“$VB$Local_myProperty”。所以在 属性 名称前面添加了 $VB$Local_。然而,从 属性 Set 存根调用按预期工作。

而且,当结果OK时,memberExpression.Member'类型是System.Reflection.RuntimePropertyInfo。而当添加奇怪的“$VB$Local_”时,memberExpression.Member 会导致 System.Reflection.RtFieldInfo 类型。

当检查上述 memberExpression 的表达式 属性 时:memberExpression.Expression,我发现其中的 Type 属性 会 - 很好行为 - 具有正确的 Container Class 名称。然而,在错误行为中,该类型 属性 将具有类似“_Closure$__X”的 'FullName' 属性 + 容器(声明)Class 名称。进一步查看此类型 属性 会发现此 FullName 由类型本身的名称“_Closure$__X”与包含正确 [=67= 的 'ReflectedType' 组合而成] 名称,导致这个奇怪的全名。这个“_Closure$__X” 顺便说一句,'X'代表一个Number。它将在第一个 属性 Get stub 中为“1”,第二个为 2,依此类推。所以:“_Closure$__1”,“_Closure$__2”...

有意见吗?

编辑:

为清楚起见,这里是代码的快照:

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

GetPropertySetProperty定义如下代码:

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

希望对您有所帮助。

如果您还记得表达式的主要用途,那么这是有道理的:表达式通常用于编译成函数。引用自身的 Get 属性 中的表达式将编译成无限循环。因此,不是那个 lambda 变成 PropertyExpression,而是在闭包上变成 FieldExpression

GetProperty(Of Result)(Function() RegisteredServer)GetProperty(Of Result)(Function() Me.RegisteredServer) 相同,因此编译器附上了 'this' (Me) 引用。闭包周围的字段表达式可能会导致访问编译器生成的 class,名称很奇怪。

在您的情况下,您并不真正关心 'this' 引用,您只是想要一种以强类型方式引用 属性 的方法。你可以通过添加一个显式参数来做到这一点,这样你就不会包含任何东西:

    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

...然后 GetProperty(Of Result)(Function() RegisteredServer) 变为 GetProperty(Of YourClass, Result)(Function(c) c.RegisteredServer)


编辑: 经过进一步思考,您不需要 TClass 类型变量:

    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

... 其中 X 是您的 class 的名称。这意味着您可以从 GetProperty 调用中删除类型注释:因此,您现在可以执行 GetProperty(Function(c) c.RegisteredServer).

而不是 GetProperty(Of Result)(Function() RegisteredServer)GetProperty(Of YourClass, Result)(Function(c) c.RegisteredServer)