简体   繁体   中英

Use type to access properties instead of concrete instance

I'm having trouble figuring out how to ask this question (the title is not worded well), so let me start with an example using the NameOf() method, because it does something similar to what I'm trying to accomplish.

If I have a class like this:

Public Class MyClass
    Public Property Foo As Bar
End Class

and then I declare an instance of it Dim instance As New MyClass , I can then use the NameOf() method in one of two ways:

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

I would like to implement a method that can accept similar types of parameters, but I cannot figure out how. Here is the (extension) method I wrote:

<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

Using my above example, I can use it like this:

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

I would like to create another version of this method that is not an extension method, but rather a static method that can be called like (or similar to) this:

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

This way I'd be able to use the function without actually creating an instance of MyClass first. And this is where I need help. Since MyClass is not a static class, I'm not able to put MyClass.Foo in the function call. My first thought was to use Reflection, but I can't figure out how. Is there a parameter type I could use to allow me to do this (and if so, how would that look)? Or is this just a pipe-dream and not possible?

You can't pass in a direct reference to a property on a type, but you can cheat using lambda expressions (Function delegates) by typing the parameter to the delegate (essentially a static open instance delegate ).

Here is my module to return a path reference from an instance or from a type:

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

Which you can use like so:

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

NOTE: VB also seems to rename local variables so I added some code to drop the $VB$Local_ prefix from names - this can be removed if not needed.

PS This seems a lot cleaner/simpler in C# ;)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM