The question itself is pretty simple, but I consider it good form to give context and the current pain points. If you just want to see the question, skip to the bottom code snippet and read the code comment inside it.
Some background: We're having a backend model that consist of structured key-value pairs. We have numerous views on top these data. We want these views to be write-through and read-through. Our current implementation allows us to use expression syntax
public class Car
{
Car(Record car_record)
{
Color = new Color<ColorEnum>(()=>car_record.GetFactoryAsset("Manufacturer","Color").Value);
}
}
The point is that the color class is agnostic as to the various ways of obtaining the correct underlying parameter. All these parameter inherit from a base class. Its constructor takes the expression and compiles an Action and a Func for getting and setting the underlying string.
public class WrappedPropBase
{
protected Action<string> Setter;
protected Func<string> Getter;
public WrappedPropBase(Expression<Func<string>> expr)
{
var memberExpression = (MemberExpression)expr.Body;
var instanceExpression = memberExpression.Expression;
var parameter = Expression.Parameter(typeof(string));
if (memberExpression.Member is PropertyInfo propertyInfo)
{
Setter = Expression.Lambda<Action<string>>(Expression.Call(instanceExpression, propertyInfo.GetSetMethod(), parameter), parameter).Compile();
Getter = Expression.Lambda<Func<string>>(Expression.Call(instanceExpression, propertyInfo.GetGetMethod())).Compile();
}
else if (memberExpression.Member is FieldInfo fieldInfo)
{
Setter = Expression.Lambda<Action<string>>(Expression.Assign(memberExpression, parameter), parameter).Compile();
Getter = Expression.Lambda<Func<string>>(Expression.Field(instanceExpression, fieldInfo)).Compile();
}
}
}
This is very elegant and allows the definition of the model to be terse and readable. However, the performance is not good. Since it calls the compile step twice for every property of every instance in a rather large model structure.
The performance is now such that I am basically forced to rewrite this. An obvious solution would be to accept a Action and Func when instantiating the model.
Thus the car model would look something like this.
public class Car
{
Car(Record car_record)
{
Color = new Color<ColorEnum>(()=>car_record.GetFactoryAsset("Manufacturer","Color").Value,
s =>car_record.GetFactoryAsset("Manufacturer","Color").Value=s)
}
}
It may not look like much of a change, but it is a duplication that could be avoided and causes a clutter.
What I would like to do is to have the runtime compile step to not execute for every car instance, but only for car model type.
Such that instead of accepting a
()=> car.GetTheCorrectRecord().Value
... and compiling an
Action<string> and a Func<string>
I would instead have a generic Wrapper that would accept Car as a type parameter and accept
car => car.GetTheCorrectRecord().Value
....and produce
Func<Car,string> and an Action<Car String
....giving me getter and setter that would be reusable for all instances for Car. So I am hoping to produce a base class looking like this:
public class WrappedPropBase<TModelType>
{
protected Action<TModelType,string> Setter;
protected Func<TModelType,string> Getter;
public WrappedPropBase(Expression<Func<TModelType,string>> expr)
{
// What do I write here to produce Getter and Setter that will read/write to the string property referenced in the passed Expression instance?
}
}