表达式(Func(Of T))。Body.Member.Name奇怪" $ vb $ local _"如果在Property Get Accessor中使用,则添加

时间:2015-03-16 18:05:12

标签: .net vb.net reflection lambda linq-expressions

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

以某种方式,通过Lambda表达式提供对类中的属性名称的引用,而不是名称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表达式。

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

然而,在VB .Net中我注意到以下奇怪的行为:当调用Property Get内部的方法时,通过诸如(Function() myProperty)之类的方式提供属性,memberExprisson.Member.Name结果为:“$ VB $ Local_myProperty”。所以在属性名称前添加了$ VB $ Local_。然而,从Property Set存根调用按预期工作。

更重要的是,当结果正常时,memberExpression.Member'类型为System.Reflection.RuntimePropertyInfo。而当添加奇怪的“$ VB $ Local_”时,memberExpression.Member会产生System.Reflection.RtFieldInfo类型。

当检查上面提到的memberExpression的表达式属性时,memberExpression.Expression,我发现其Type属性 - 在良好行为上 - 具有正确的容器类名称。但是,在错误行为中,Type属性将具有类似“_Closure $ __ X”+容器(声明)类名称的“FullName”属性。进一步查看此Type属性会显示此FullName由Type本身的Name组成,其中“_Closure $ __ X”与包含正确Class Name的'ReflectedType'相结合,从而产生这个奇怪的FullName。顺便说一句,这是“_Closure $ __ X”,'X'代表一个数字。它将在第一个Property Get存根中为'1',为第二个存在时为2,依此类推。所以:“_ Close $ __ 1”,“_ Close $ __ 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

GetProperty SetProperty在以下代码中定义:

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

希望这有帮助。

1 个答案:

答案 0 :(得分:2)

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

GetProperty(Of Result)(Function() RegisteredServer)GetProperty(Of Result)(Function() Me.RegisteredServer)相同,因此编译器会将&#39;这个&#39;包含在内。 (Me)参考。闭包周围的字段表达式可能导致访问编译器生成的类,名称很奇怪。

在你的情况下,你并不真正关心这个&#39;这个&#39;参考,您只想要一种以强类型方式引用Property的方法。您可以通过添加显式参数来执行此操作,这样您就不会包含任何内容:

    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是您班级的名称。这意味着您可以从GetProperty来电中删除类型注释:现在您可以执行以下操作:GetProperty(Of Result)(Function() RegisteredServer)GetProperty(Of YourClass, Result)(Function(c) c.RegisteredServer),而不是GetProperty(Function(c) c.RegisteredServer)或{{1}}。