使用类型而不是具体实例来访问属性

Use type to access properties instead of concrete instance

我不知道怎么问这个问题(标题措辞不好),所以让我从一个使用 NameOf() 方法的例子开始,因为它做的事情和我的类似我正在努力完成。

如果我有这样的class:

Public Class MyClass
    Public Property Foo As Bar
End Class

然后我声明它的一个实例 Dim instance As New MyClass,然后我可以通过以下两种方式之一使用 NameOf() 方法:

' Both of these lines will print "Foo"
Console.WriteLine(NameOf(instance.Foo))
Console.WriteLine(NameOf(MyClass.Foo))

我想实现一个可以接受相似类型参数的方法,但我不知道如何实现。 这是我写的(扩展)方法:

<Extension()>
Public Function GetPathOfProperty(Of T As Class, TProp)(myObj As T, prop As Expression(Of Func(Of TProp))) As String
    Dim result As String = String.Empty
    Dim foundName As String = String.Empty
    Dim className As String = myObj.GetType().Name
    Dim p As MemberExpression = TryCast(prop.Body, MemberExpression)

    While p IsNot Nothing
        If className = p.Member.DeclaringType.Name Then
            foundName = className + "."
        End If

        result = foundName + p.Member.Name + "." + result
        If Not String.IsNullOrEmpty(foundName) Then
            Exit While
        End If
        p = TryCast(p.Expression, MemberExpression)
    End While

    Return IIf(String.IsNullOrEmpty(foundName), "", result.Substring(0, result.Length - 1))
End Function

使用我上面的例子,我可以这样使用它:

' Prints "MyClass.Foo"
Console.WriteLine(instance.GetPathOfProperty(Function() instance.Foo)) 
' Prints "MyClass.Foo.SomeBarProperty"
Console.WriteLine(instance.GetPathOfProperty(Function() instance.Foo.SomeBarProperty)) 

我想创建此方法的另一个版本,它不是扩展方法,而是可以像(或类似)这样调用的静态方法:

Console.WriteLine(GetPathOfProperty(Function() MyClass.Foo) ' Prints "MyClass.Foo"

这样我就可以使用该函数而无需首先实际创建 MyClass 的实例。这就是我需要帮助的地方。由于 MyClass 不是静态的 class,我无法将 MyClass.Foo 放入函数调用中。我的第一个想法是使用反射,但我不知道如何使用。是否有我可以使用的参数类型来允许我执行此操作(如果是,那看起来如何)?或者这只是一个 pipe-dream 而不可能?

你不能在类型上传递对 属性 的直接引用,但你可以通过将参数键入委托(本质上是静态 open instance delegate).

这是我的 return 来自实例或类型的路径引用的模块:

Public Module PropertyExt
    <Extension()>
    Public Function TrimStart(Byval src As String, starter As String) As String
        Return If(src.StartsWith(starter), src.Substring(starter.Length), src)
    End Function

    Public Function GetPathOfInstanceProperty(Of TRes)(e As Expression(Of Func(Of TRes))) As String
        Dim b = TryCast(e.Body, MemberExpression)
        Dim ans = ""
        Do
            ans = b.Member.Name.TrimStart("$VB$Local_") & "." & ans
            b = TryCast(b.Expression, MemberExpression)
        Loop While (b IsNot Nothing)
        Return ans.TrimEnd(".")
    End Function

    Public Function GetPathOfTypeProperty(Of T,TRes)(e As Expression(Of Func(Of T,TRes))) As String
        Dim b = TryCast(e.Body, MemberExpression)
        Dim ans = ""
        Do
            ans = b.Member.Name.TrimStart("$VB$Local_") & "." & ans
            Dim tryB = TryCast(b.Expression, MemberExpression)
            If tryB Is Nothing Then
                Dim tryParm = TryCast(b.Expression, ParameterExpression)
                If tryParm IsNot Nothing Then
                    ans = tryParm.Type.Name & "." & ans
                End If
            End If
            b = tryB
        Loop While (b IsNot Nothing)
        Return ans.TrimEnd(".")
    End Function
End Module

你可以这样使用:

Dim cf = New CInstance()
Dim ipe = PropertyExt.GetPathOfInstanceProperty(Function() cf.Foo.SomeBarProperty)
Dim tpe = PropertyExt.GetPathOfTypeProperty(Function(i As CInstance) i.Foo.SomeBarProperty)

注意:VB 似乎也重命名局部变量,因此我添加了一些代码以删除名称中的 $VB$Local_ 前缀 - 如果不需要,可以将其删除。

PS 这在 C# 中似乎很多 cleaner/simpler ;)