I'm using the following code to create an Expression based property setter:
Public Function PropertySetExpression(Of TObjectType, TPropertyType)(propertyName As String) As Expression(Of Action(Of TObjectType, TPropertyType))
Dim paramExpression = Expression.Parameter(GetType(TObjectType), "value")
Dim propertyExpression = Expression.[Property](paramExpression, propertyName)
Dim getter = Expression.Lambda(Of Func(Of TObjectType, TPropertyType))(propertyExpression, paramExpression)
Dim member = CType(getter.Body, MemberExpression)
Dim param = Expression.Parameter(GetType(TPropertyType), "value")
Return Expression.Lambda(Of Action(Of TObjectType, TPropertyType))(Expression.Assign(member, param), getter.Parameters(0), param)
End Function
And this works perfectly if TProperty
is specified at compile time. But sometimes I need a more flexible architecture, and want to use Object
instead of a more specific type, and this also works fine if TProperty
is a reference type. But if it's a value type, I get this error:
System.ArgumentException: 'Expression of type 'System.Int32' cannot be used for return type 'System.Object''
In this answer to a similar question, but about a property getter, Jon Skeet explains how to fix this for a property getter, by inserting a Convert expression to box the value type to an object, but I can't figure out how to apply this in the reverse direction to unbox the object before setting back into a value type.
How might I do this?
Update, as requested by comment:
A normal property setter would be something like:
obj.Id = 3
The above code is so that I could get a function dynamically to accomplish the same thing:
Dim setter = PropertySetExpression(Of TMyObject, Integer)("Id").Compile()
...
setter(obj, 3)
The reason to do this would be in scenarios where I want to be able to set properties specified in, say, a text file, but with the fast performance of a property setter.
As I say in the post, this works fine. But sometimes I don't even know the data type at compile time, so I want to do:
Dim setter = PropertySetExpression(Of TMyObject, Object)(prop_name).Compile()
And let the setter deal with the boxing/unboxing from the actual data type (Integer
in this case) to Object
.