I'm trying to build a string for a class that contains a list of all it's property names and their respective values for that instance.
Per the question Get Property Info from an object without giving the property name as string, I'm getting the property name by using an Expression
of Type Func
Of Type T, Object
like this:
Private Function GetPropertyValuePair(Of T)(lambda As Expression(Of Func(Of T, Object))) As String
Dim body As Expression = lambda
'ensure we have the right type of thing
If TypeOf body Is LambdaExpression Then body = DirectCast(body, LambdaExpression).Body
If TypeOf body Is UnaryExpression Then body = DirectCast(body, UnaryExpression).Operand
If body.NodeType <> ExpressionType.MemberAccess Then Throw New InvalidOperationException()
'get property info
Dim memberInfo = DirectCast(body, MemberExpression).Member
Dim propInfo = DirectCast(memberInfo, PropertyInfo)
Return propInfo.Name + " - " + propInfo.GetValue(Me, Nothing)
End Function
In this manner, I'm getting the current value by taking the PropertyInfo
, and using GetValue
which searches the current instance for that property.
Then I can call it like this:
GetPropertyValuePair(Of Foo)(Function(x) x.Bar)
Which will produce:
Bar - WhateverTheValueIsForBar
The problem is when I have is when the property is nested, GetValue
will throw the exception:
Object does not match target type.
For example, in running the following expression:
GetPropertyValuePair(Of Foo)(Function(x) x.Bar.Car)
The property info will be of of type Car
, which is not found directly on Foo
.
Can I get the instance value from the original lambda expression without having to do the search with GetValue in the first place?
This is possible with the MVC @Html.EditorFor
helper, so there must be some mechanism for delivering the value of the passed in function for something like this:
@Html.EditorFor(Function(model) model.Bar.Car)
Any Ideas?
I pieced together a solution based on How to evaluate a lambda expression.
You can
Compile
the expression to produce the runnable function inside of it.Since I'm still working my way through lambdas, here's a little more by way of explanation:
Inside the Expression, we declared the function signature to look like this
Func(Of T, Object)
When we called the method, we told the function evaluation to behave like this
x => x.Bar.Car
Compiling will give us a new function, stored as a local variable, that will behave like this:
So we haven't actually passed in the instance at all. We've just passed in the definition for what should happen when we call this anonymous function and pass in something of type T.
So we still need to pull in
Me
. In this case, because this function happens to be sitting inside the same class, we cheated a little since the method already had access to the instance variables. If it was abstracted out somewhere else, the instance itself would have to be passed in as an additional parameter.