11
\$\begingroup\$

This generic function invokes a property of a given object by name:

  Private Shared Function GetPropertyValueByName(Of TObject, TProperty)(
        ByVal T As TObject,
        ByVal PropertyName As String) As TProperty

        Dim result = GetType(TObject).GetProperties() _
            .Where(
                Function(p) (p.Name = PropertyName) _
                    AndAlso p.CanRead() _
                    AndAlso (p.PropertyType Is GetType(TProperty))
                ).FirstOrDefault()

        If (result IsNot Nothing) Then
            Return CType(result.GetValue(T), TProperty)
        End If

        Return Nothing

    End Function

Can this function be improved in some way? Can it generate some sort of issues I have to be aware of? Can it be improved in terms of readability?

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Is there some reason you're iterating over GetProperties instead of using GetProperty or InvokeMember? I'm hesitant to post an answer showing how to do these if there's some deeper reason you're using this approach. \$\endgroup\$
    – Random832
    Commented Dec 21, 2015 at 18:30
  • \$\begingroup\$ @Random832 There isn't a rational reason. I'd like to read your answer \$\endgroup\$
    – user42412
    Commented Dec 21, 2015 at 23:23

3 Answers 3

3
\$\begingroup\$

Normally, to access one specific property (rather than enumerating all of them as is done for e.g. the form designer's properties view), you wouldn't use GetProperties. We can therefore simplify the code:

Private Shared Function GetPropertyValueByName(Of TObject, TProperty) (
    ByVal T As TObject,
    ByVal PropertyName As String) As TProperty

    Dim result = GetType(TObject).GetProperty(PropertyName, GetType(Property), {})

    If (result IsNot Nothing) Then
        Return CType(result.GetValue(T), TProperty)
    End If

    Return Nothing

End Function

Since CLI types can only have a single property with a given name (you can't have multiple properties overloaded by return type), you can simplify this further if you're willing to accept that you may be getting a property of some other type and then casting it to the TProperty type.

Private Shared Function GetPropertyValueByName(Of TObject, TProperty) (
    ByVal T As TObject,
    ByVal PropertyName As String) As TProperty

    Const flag = BindingFlags.Public Or BindingFlags.Instance or BindingFlags.GetProperty
    Dim value = GetType(TObject).InvokeMember(PropertyName, flag, Nothing, T, {})
    Return CType(value, TProperty)
End Function

If you want to return Nothing if the property is not found or has the wrong type, you will need to catch exceptions.

You can also avoid passing in TObject by simply using the .GetType() method on the object, but that changes the semantics further.

NOTE: It's been a while since I've coded in VB, and I'm not 100% sure that {} is the correct syntax for an empty array whose type can be inferred. It is intended as an empty array of Type in the first example, and of Object in the second example.

\$\endgroup\$
8
\$\begingroup\$

LINQ has two syntaxes to offer. You've used the method syntax, which I find very neat and useful, but for simpler queries I find the query syntax looks much less bulky in VB.NET:

Dim result = (From p In GetType(TObject).GetProperties()
              Where p.Name = PropertyName _
                  AndAlso p.CanRead() _
                  AndAlso p.PropertyType Is GetType(TProperty)
              Select p) _
             .FirstOrDefault()

It removes the inline Function(p) anonymous function declaration, and leaves it up to the compiler to generate it, which in turns frees your code from some of the boilerplate.

\$\endgroup\$
4
\$\begingroup\$

"Can it generate some sort of issues I have to be aware of?"

Yes, it will throw a TargetParameterCountException if the property contains parameters, so you might want to add the following line to the Where predicate.

AndAlso (p.GetIndexParameters().Length = 0)

This will exclude properties like the one shown below.

Public Property Pixel(x As Int32, y As Int32) As Color
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.