Cannot Create Generator using Expressions - c#

What I have:
public class HubGroup: HubObject
{
public HubGroup(elnGroup group)
{//do stuff}
}
public class elnGroup: elnObject
{
//has properties
}
My requirement is when I give 2 types to a method, it will generate a function that will accept object of elnGroup as a parameter and return a new Instance of HubGroup. I tried a lot of things but I couldn't find a way that I can do it fast (these generator functions will be running several times)
I know you will say use generics but my Types are generated at runtime. All I have is the Base classes for both the Types which I can explicity declare. The code you see below is bits of pieces i have, just to give u a heads up of what I happening.
So I call it like:
public class ApiDataHandler
{
//this will be called by a method to give json content for objEln
public void SoFunny<T>(string strContent, T objHub) where T : HubObject
{
if (typeof(T) == typeof(HubGroup))
{
if (string.IsNullOrEmpty(strContant))
{
throw new Exception("Cannot parse null/empty string! ---ApiDataHandler---");
}
var objEln = JsonConvert.DeserializeObject<elnGroup>(strContant);
GetHubObjectGenerator(objEln.GetType(), objHub.GetType());
}
}
}
What I want to create:
Func<object,object> generator = (input) => {var bObj = input as BObject;
var aObj = new AObject(bObj);
return aObj;
}
I have done this: but it keeps saying:
InvalidOperationException: variable 'objEln' of type 'ElnHub.HubObjectModel.elnGroup' referenced from scope '', but it is not defined
//also inside ApiData Handler class
public Func<object, object> GetHubObjectGenerator(Type elnObjectType, Type hubObjectType)
{
ParameterExpression inputParam = Expression.Parameter(typeof(object), "input");
ParameterExpression objCastedAsEln = Expression.Parameter(elnObjectType, "objEln");
ParameterExpression objHub = Expression.Parameter(hubObjectType, "objHub");
var cast = Expression.TypeAs(inputParam, elnObjectType);
var assignCast = Expression.Assign(objCastedAsEln, cast);
var constructor = hubObjectType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { elnObjectType }, null);
var callingConstructor = Expression.New(constructor, new[] { objCastedAsEln });
var assignNewObj = Expression.Assign(objHub, callingConstructor);
var bodyBlock = Expression.Block(new[] { inputParam },
assignCast,
assignNewObj,
objHub
);
var l = Expression.Lambda<Func<object, object>>(
bodyBlock,
inputParam
);
Func<object, object> HubObjectGenerator = l.Compile();
return HubObjectGenerator;
}
I have also tried this where i send generic types, but couldnt find my way. A bit lost here:
public Func<T,T1> GetHubObjectGenerator<T,T1>() where T : elnObject where T1 : HubObject
{
ParameterExpression argParam = Expression.Parameter(typeof(T), "objEln");
var constructor = typeof(T1).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance,null,new[] { typeof(T) },null);
Func<T, T1> HubObjectGenerator = Expression.Lambda<Func<T, T1>>(
Expression.New(constructor, new[] { argParam, }),
argParam
).Compile();
return HubObjectGenerator;
}

You want to write something like:
Func<object,object> generator = (input) =>
{
return new AObject((BObject)input);
}
You need something like:
public Func<object, object> GetHubObjectGenerator(Type elnObjectType, Type hubObjectType)
{
var inputParameter = ExpressionParameter(typeof(object), "input");
var castInput = Expression.Convert(inputParameter, elnObjectType);
var constructor = hubObjectType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { elnObjectType }, null);
var instantiation = Expression.New(constructor, castInput);
var lambda = Expression.Lambda<Func<object, object>>(instantiation, inputParameter);
return lambda.Compile();
}
You can use generics here easily also, you don't even need a cast:
public Func<THub, TEln> GetHubObjectGenerator<THub, TEln>() where THub : HubObject, TEln : elnObject
{
var inputParameter = ExpressionParameter(typeof(TEln), "input");
var constructor = typeof(THub).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(TEln) }, null);
var instantiation = Expression.New(constructor, inputParameter);
var lambda = Expression.Lambda<Func<THub, TEln>>(instantiation, inputParameter);
return lambda.Compile();
}
I didn't use variables or Expression.Block here, as there's no need. If you did want to create intermediate variables, use Expression.Variable (Expression.Parameter is for input parameters only). Something like:
public Func<object, object> GetHubObjectGenerator(Type elnObjectType, Type hubObjectType)
{
var inputParameter = ExpressionParameter(typeof(object), "input");
var castInputVar = Expression.Variable(elnObjectType, "eln");
var castInput = Expression.Convert(inputParameter, elnObjectType);
var castInputAssign = Expression.Assign(castInputVar, castInput);
var constructor = hubObjectType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { elnObjectType }, null);
var hubObjectVar = Expression.Variable(hubObjectType, "hub");
var instantiation = Expression.New(constructor, castInputVar);
var hubObjectAssign = Expression.Assign(hubObjectVar, instantiation);
var block = Expression.Block(new[] { castInputVar, hubObjectVar },
castInputAssign,
hubObjectAssign,
hubObject);
var lambda = Expression.Lambda<Func<object, object>>(block, inputParameter);
return lambda.Compile();
}

Related

How can I dynamically set up a mock for an enum property?

I'm trying to create a mock dynamically based on xml data. This works fine for most types, but enums turn out to be a bit tricky. The ??? in my code expects the type of the enum. But obviously the type is not known at compile time, so I'll have to resort to reflection. I'll probably have to use MakeGenericMethod to invoke Expression.Lambda directly or indirectly, but that seems to just move the problem as mock.Setup expects a compile time type as well. That's where I get stuck. Any help is appreciated.
public static Mock<T> DeserializeMock<T>(XElement node)
where T : class
{
var mock = new Mock<T>();
foreach (var property in PropertyInfoHelper.EnumeratePublicAndInternalProperties(typeof(T)))
{
var attribute = node.Attribute(property.Name);
if (property.PropertyType == typeof(string))
{
var parameter = Expression.Parameter(typeof(T));
var body = Expression.PropertyOrField(parameter, property.Name);
var propertyExpression = Expression.Lambda<Func<T, string>>(body, parameter);
mock.Setup(propertyExpression).Returns(attribute.Value);
}
// ... other types omitted for brevity...
else if (property.PropertyType.IsEnum)
{
var parameter = Expression.Parameter(typeof(T));
var body = Expression.PropertyOrField(parameter, property.Name);
var propertyExpression = Expression.Lambda<Func<T, ???>>(body, parameter);
mock.Setup(propertyExpression).Returns(convertToEnum(attribute.Value, property.PropertyType));
}
}
return mock;
}
You would end up having to use reflection to get the types for the setup
//...omitted for brevity...
else if (property.PropertyType.IsEnum) {
var parameter = Expression.Parameter(typeof(T));
var body = Expression.PropertyOrField(parameter, property.Name);
var delegateType = typeof(Func<,>).MakeGenericType(typeof(T), property.PropertyType);
var propertyExpression = Expression.Lambda(delegateType, body, parameter);
var value = convertToEnum(attribute.Value, property.PropertyType);
var setup = mock.GetType().GetMethods()
.FirstOrDefault(m => m.Name == "Setup" && m.ContainsGenericParameters)
.MakeGenericMethod(property.PropertyType);
var result = setup.Invoke(mock, new object[] { propertyExpression });
var returns = result.GetType().GetMethod("Returns", new[] { property.PropertyType });
returns?.Invoke(result, new object[] { value });
}
//...omitted for brevity...
I also assume based on the original code that all the members being setup on the mock are either virtual or abstract members.
Proof of concept
[TestClass]
public class MyTestClass {
[TestMethod]
public void MyTestMethod() {
//Arrange
XElement element = new XElement("root",
new XAttribute("MyProperty1", "Hello World"),
new XAttribute("MyEnumProperty", "Value2")
);
//Act
var mock = DeserializeMock<MyClass>(element);
//Assert
var actual = mock.Object;
actual.MyEnumProperty.Should().Be(MyEnum.Value2);
actual.MyProperty1.Should().Be("Hello World");
}
public class MyClass {
public virtual string MyProperty1 { get; set; }
public virtual MyEnum MyEnumProperty { get; set; }
}
public enum MyEnum {
Value1,
Value2
}
//...
}

Variable 'x.Sub' of type 'SubType' referenced from scope '' but it is not defined error

Check this fiddle for the error: https://dotnetfiddle.net/tlz4Qg
I have two classes like this:
public class ParentType{
private ParentType(){}
public int Id { get; protected set; }
public SubType Sub { get; protected set; }
}
public class SubType{
private SubType(){}
public int Id { get; protected set; }
}
I am going to transform a multilevel anonymous expression to a multilevel non-anonymous expression. To achieve this I have an expression like the below-mentioned one:
x => new
{
x.Id,
Sub = new
{
x.Sub.Id
}
}
To achieve that goal, I have transformed it to an expression like this:
x => new ParentType()
{
Id = x.Id,
Sub = new SubType()
{
Id = x.Sub.Id
},
}
But when I call Compile() method, I get the following error:
Variable 'x.Sub' of type 'SubType' referenced from scope '' but it is not defined
Here is my visitor class:
public class ReturnTypeVisitor<TIn, TOut> : ExpressionVisitor
{
private readonly Type funcToReplace;
private ParameterExpression currentParameter;
private ParameterExpression defaultParameter;
private Type currentType;
public ReturnTypeVisitor() => funcToReplace = typeof(Func<,>).MakeGenericType(typeof(TIn), typeof(object));
protected override Expression VisitNew(NewExpression node)
{
if (!node.Type.IsAnonymousType())
return base.VisitNew(node);
if (currentType == null)
currentType = typeof(TOut);
var ctor = currentType.GetPrivateConstructor();
if (ctor == null)
return base.VisitNew(node);
NewExpression expr = Expression.New(ctor);
IEnumerable<MemberBinding> bindings = node.Members.Select(x =>
{
var mi = currentType.GetProperty(x.Name);
//if the type is anonymous then I need to transform its body
if (((PropertyInfo)x).PropertyType.IsAnonymousType())
{
//This section is became unnecessary complex!
//
var property = (PropertyInfo)x;
var parentType = currentType;
var parentParameter = currentParameter;
currentType = currentType.GetProperty(property.Name).PropertyType;
currentParameter = Expression.Parameter(currentType, currentParameter.Name + "." + property.Name);
//I pass the inner anonymous expression to VisitNew and make the non-anonymous expression from it
var xOriginal = VisitNew(node.Arguments.FirstOrDefault(a => a.Type == property.PropertyType) as NewExpression);
currentType = parentType;
currentParameter = parentParameter;
return (MemberBinding)Expression.Bind(mi, xOriginal);
}
else//if type is not anonymous then simple find the property and make the memberbinding
{
var xOriginal = Expression.PropertyOrField(currentParameter, x.Name);
return (MemberBinding)Expression.Bind(mi, xOriginal);
}
});
return Expression.MemberInit(expr, bindings);
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
if (typeof(T) != funcToReplace)
return base.VisitLambda(node);
defaultParameter = node.Parameters.First();
currentParameter = defaultParameter;
var body = Visit(node.Body);
return Expression.Lambda<Func<TIn, TOut>>(body, currentParameter);
}
}
And use it like this:
public static Expression<Func<Tin, Tout>> Transform<Tin, Tout>(this Expression<Func<Tin, object>> source)
{
var visitor = new ReturnTypeVisitor<Tin, Tout>();
var result = (Expression<Func<Tin, Tout>>)visitor.Visit(source);
return result;// result.Compile() throw the aforementioned error
}
Here is the extension methods used inside my Visitor class:
public static ConstructorInfo GetPrivateConstructor(this Type type) =>
type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
// this hack taken from https://stackoverflow.com/a/2483054/4685428
// and https://stackoverflow.com/a/1650895/4685428
public static bool IsAnonymousType(this Type type)
{
var markedWithAttribute = type.GetCustomAttributes(typeof(CompilerGeneratedAttribute), inherit: false).Any();
var typeName = type.Name;
return markedWithAttribute
&& (typeName.StartsWith("<>") || type.Name.StartsWith("VB$"))
&& typeName.Contains("AnonymousType");
}
Update
Here is the .Net Fiddle link for the problem: https://dotnetfiddle.net/tlz4Qg
Update
I have removed the extra codes that seems to be out of the problem scope.
The cause of the problem in question is the line
currentParameter = Expression.Parameter(currentType, currentParameter.Name + "." + property.Name);
inside VisitNew method.
With your sample, it creates a new parameter called "x.Sub", so if we mark the parameters with {}, the actual result is
Sub = new SubType()
{
Id = {x.Sub}.Id
},
rather than expected
Sub = new SubType()
{
Id = {x}.Sub.Id
},
In general you should not create new ParameterExpressions except when remapping lambda expressions. And all newly created parameters should be passed to Expression.Lambda call, otherwise they will be considered "not defined".
Also please note that the visitor code has some assumptions which doesn't hold in general. For instance
var xOriginal = Expression.PropertyOrField(currentParameter, x.Name);
won't work inside nested new, because there you need access to a member of the x parameter like x.Sub.Id rather than x.Id. Which is basically the corersonding expression from NewExpression.Arguments.
Processing nested lambda expressions or collection type members and LINQ methods with expression visitors requires much more state control. While converting simple nested anonymous new expression like in the sample does not even need a ExpressionVisitor, because it could easily be achieved with simple recursive method like this:
public static Expression<Func<Tin, Tout>> Transform<Tin, Tout>(this Expression<Func<Tin, object>> source)
{
return Expression.Lambda<Func<Tin, Tout>>(
Transform(source.Body, typeof(Tout)),
source.Parameters);
}
static Expression Transform(Expression source, Type type)
{
if (source.Type != type && source is NewExpression newExpr && newExpr.Members.Count > 0)
{
return Expression.MemberInit(Expression.New(type), newExpr.Members
.Select(m => type.GetProperty(m.Name))
.Zip(newExpr.Arguments, (m, e) => Expression.Bind(m, Transform(e, m.PropertyType))));
}
return source;
}

Dynamic constructor in C#

I am trying to write a method GetDynamicConstructor<T> which will return, essentially, a smart constructor for the given class. It will accept an array of strings as parameters and parse them as the appropriate type (given existing constructor data).
public void Init()
{
DynamicConstructor<MyClass> ctor = GetDynamicConstructor<MyClass>();
MyClass instance = ctor(new string[] { "123", "abc" }); // parse "123" as int
}
public delegate T DynamicConstructor<T>(string[] args);
public DynamicConstructor<T> GetDynamicConstructor<T>()
{
ConstructorInfo originalCtor = typeof(T).GetConstructors().First();
ParameterInfo[] paramsInfo = originalCtor.GetParameters();
for (int i = 0; i < paramsInfo.Length; i++) {
Type paramType = paramsInfo[i].ParameterType;
// This is as far as I got :D
}
return null;
}
public class MyClass
{
int n;
string s;
public MyClass(int n, string s)
{
this.n = n;
this.s = s;
}
}
What I want, basically, is to construct from MyClass a method which looks like this.
public MyClass Example(string[] args)
{
return new MyClass(int.Parse(args[0]), args[1]);
}
There will only be basic types here so I can count on a Parse existing for the types I might run into.
How can I write the body of GetDynamicConstructor<T>?
This method already exists in the System.Activator class:
public static object CreateInstance (Type type, params object[] args);
Of course a constructor overload corresponding to the actual parameter data must exist. You can use Convert.ChangeType Method (Object, Type) to change the type of the parameters.
See: CreateInstance(Type, Object[]) on learn.microsoft.com.
Activator.CreateInstance has 16 different overloads.
Depending on how exactly you want use this there are a couple ways to do it. Steve16351 has one way which is to create a delegate to a method that does all the reflection at execution time. Another way would be to generate a delegate which looks like your Example method at execution time, which is then cached. The difference would be that the former can be more flexible, while the latter would be faster. Using reflection on each execution can take into account which conversions are successful before selecting the constructor. While a compiled delegate has to know which constructor to select before arguments are available, it will have performance characteristics more like a method that had been written natively in C#. Below is in an implementation to generate the delegate using expression trees. You'd want to cache this for each type for maximum performance:
using System.Linq.Expressions;
public static DynamicConstructor<T> GetDynamicConstructor<T>()
{
ConstructorInfo originalCtor = typeof(T).GetConstructors().First();
var parameter = Expression.Parameter(typeof(string[]), "args");
var parameterExpressions = new List<Expression>();
ParameterInfo[] paramsInfo = originalCtor.GetParameters();
for (int i = 0; i < paramsInfo.Length; i++)
{
Type paramType = paramsInfo[i].ParameterType;
Expression paramValue = Expression.ArrayIndex(parameter, Expression.Constant(i));
if (paramType.IsEnum)
{
var enumParse = typeof(Enum).GetMethod("Parse", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(Type), typeof(string) }, null);
var call = Expression.Call(null, enumParse, new[] { Expression.Constant(paramType), paramValue });
paramValue = Expression.Convert(call, paramType);
}
else if (paramType != typeof(string))
{
var parseMethod = paramType.GetMethod("Parse", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(string) }, null);
if (parseMethod == null)
{
throw new Exception($"Cannot find Parse method for type {paramType} (parameter index:{i})");
}
paramValue = Expression.Call(null, parseMethod, new[] { paramValue });
}
parameterExpressions.Add(paramValue);
}
var newExp = Expression.New(originalCtor, parameterExpressions);
var lambda = Expression.Lambda<DynamicConstructor<T>>(newExp, parameter);
return lambda.Compile();
}
Note that I added handling of enums since Parse can't be called the same way as other simple types.
Update:
Based on comments here is an expanded version to emit a non-generic delegate that will handle default parameter values:
public static DynamicConstructor GetDynamicConstructor(Type type)
{
ConstructorInfo originalCtor = type.GetConstructors().First();
var parameter = Expression.Parameter(typeof(string[]), "args");
var parameterExpressions = new List<Expression>();
ParameterInfo[] paramsInfo = originalCtor.GetParameters();
for (int i = 0; i < paramsInfo.Length; i++)
{
Type paramType = paramsInfo[i].ParameterType;
// added check for default value on the parameter info.
Expression defaultValueExp;
if (paramsInfo[i].HasDefaultValue)
{
defaultValueExp = Expression.Constant(paramsInfo[i].DefaultValue);
}
else
{
// if there is no default value, then just provide
// the type's default value, but we could potentially
// do something else here
defaultValueExp = Expression.Default(paramType);
}
Expression paramValue;
paramValue = Expression.ArrayIndex(parameter, Expression.Constant(i));
if (paramType.IsEnum)
{
var enumParse = typeof(Enum).GetMethod("Parse", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(Type), typeof(string) }, null);
var call = Expression.Call(null, enumParse, new[] { Expression.Constant(paramType), paramValue });
paramValue = Expression.Convert(call, paramType);
}
else if (paramType != typeof(string))
{
var parseMethod = paramType.GetMethod("Parse", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(string) }, null);
if (parseMethod == null)
{
throw new Exception($"Cannot find Parse method for type {paramType} (parameter index:{i})");
}
paramValue = Expression.Call(null, parseMethod, new[] { paramValue });
}
// here we bounds check the array and emit a conditional expression
// that will provide a default value if necessary. Equivalent to
// something like i < args.Length ? int.Parse(args[i]) : default(int);
// Of course if the parameter has a default value that is used instead,
// and if the target type is different (long, boolean, etc) then
// we use a different parse method.
Expression boundsCheck = Expression.LessThan(Expression.Constant(i), Expression.ArrayLength(parameter));
paramValue = Expression.Condition(boundsCheck, paramValue, defaultValueExp);
parameterExpressions.Add(paramValue);
}
var newExp = Expression.New(originalCtor, parameterExpressions);
var lambda = Expression.Lambda<DynamicConstructor>(newExp, parameter);
return lambda.Compile();
}
}

Conditional method calls in an expression tree

I'm trying to add an additional method call to my expression tree, but I'm slightly confused how to implement it. Here is what I'm currently working with:
private static Action<object, object> CreateSetter(SetterInfo info)
{
var propertyInfo = info.Type.GetProperty(info.Name, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
if (propertyInfo == null)
return (s, v) => { };
var objParameter = Expression.Parameter(typeof(object));
var valueParameter = Expression.Parameter(typeof(object));
//This is the method call I'm trying to add
if (info.Name[0] == 'G' && info.Type.Name == TaxDataConstant.ParcelFeat)
{
var convertParcelFeatCall = Expression.Call(ConvertParcelFeatMethod, valueParameter, Expression.Constant(info.Name));
}
var changeTypeCall = Expression.Call(ChangeTypeMethod, valueParameter, Expression.Constant(propertyInfo.PropertyType));
var objCast = Expression.Convert(objParameter, info.Type);
var valueCast = Expression.Convert(changeTypeCall, propertyInfo.PropertyType);
var property = Expression.Property(objCast, propertyInfo);
var assignment = Expression.Assign(property, valueCast);
var lambda = Expression.Lambda<Action<object, object>>(assignment, objParameter, valueParameter);
return lambda.Compile();
}
What I want to happen is:
1) If the name of the type in my SetterInfo object is ParcelFeat and the Properties name begins with 'G' I want to call ConvertParcelFeat on valueParameter and then call ChangeType on the return.
2) If the name of the type is anything other than ParcelFeat call Changetype as normal with out the additional steps
What I'm confused is how to build the conditional. I'm assuming the way I'm doing it in the above code is wrong and I need to use something like Expression.IfThen() to to build the conditional. I'm also unsure how I can chain the method calls like I want.
You do not need in Expression.IfThen because for each specific SetterInfo you combine exactly one specific lambda instance.
Just plug in convertParcelFeatCall in proper place of your ExpressionTree and all should work just fine.
So your code might look like:
class Program
{
static void Main(string[] args)
{
var program = new Program();
var weightLambda = program.DoInternal("Weight").ToString()
== "(Param_0, Param_1) => (Convert(Param_0).Weight = Convert(ChangeType(Param_1, System.Object)))";
var goodiesLambda = program.DoInternal("Goodies").ToString()
== "(Param_0, Param_1) => (Convert(Param_0).Goodies = Convert(ChangeType(Param_1, ConvertParcelFeat(Param_1, \"Goodies\"))))";
Console.WriteLine("WeightLambda is Ok: {0}\nGoodiesLambda is Ok: {1}", weightLambda, goodiesLambda);
}
public Action<Object, Object> Do(string name)
{
return DoInternal(name).Compile();
}
public Expression<Action<object, object>> DoInternal(string name)
{
var info = new {Name = name, Type = typeof(Program)};
var propertyInfo = info.Type.GetProperty(info.Name, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
var objParameter = Expression.Parameter(typeof(object));
var valueParameter = Expression.Parameter(typeof(object));
//This is the method call I'm trying to add
Expression toBeTypeChanged;
if (info.Name[0] == 'G' && info.Type.Name == "Program")
{
toBeTypeChanged = Expression.Call(ConvertParcelFeatMethod, valueParameter, Expression.Constant(info.Name));
}
else
{
toBeTypeChanged = Expression.Constant(propertyInfo.PropertyType);
}
var changeTypeCall = Expression.Call(ChangeTypeMethod, valueParameter, toBeTypeChanged);
var objCast = Expression.Convert(objParameter, info.Type);
var valueCast = Expression.Convert(changeTypeCall, propertyInfo.PropertyType);
var property = Expression.Property(objCast, propertyInfo);
var assignment = Expression.Assign(property, valueCast);
return Expression.Lambda<Action<object, object>>(assignment, objParameter, valueParameter);
}
public object Weight { get; set; }
public object Goodies { get; set; }
public static object ChangeType(object valueParameter, object constant)
{
return null;
}
public static object ConvertParcelFeat(object valueParameter, object constant)
{
return null;
}
public MethodInfo ConvertParcelFeatMethod
{
get { return typeof(Program).GetMethod("ConvertParcelFeat"); }
}
public MethodInfo ChangeTypeMethod
{
get { return typeof(Program).GetMethod("ChangeType"); }
}
}

Reading Properties of an Object with Expression Trees

I want to create a Lambda Expression for every Property of an Object that reads the value dynamically.
What I have so far:
var properties = typeof (TType).GetProperties().Where(p => p.CanRead);
foreach (var propertyInfo in properties)
{
var getterMethodInfo = propertyInfo.GetGetMethod();
var entity = Expression.Parameter(typeof (TType));
var getterCall = Expression.Call(entity, getterMethodInfo);
var lambda = Expression.Lambda(getterCall, entity);
var expression = (Expression<Func<TType, "TypeOfProperty">>) lambda;
var functionThatGetsValue = expression.Compile();
}
The code Works well when i call functionThatGetsValue as long as "TypeOfProperty" is hardcoded. I know that I can't pass the "TypeOfPoperty" dynamically. What can I do to achive my goal?
Assuming that you're happy with a Func<TType, object> delegate (as per the comments above), you can use Expression.Convert to achieve that:
var properties = typeof(TType).GetProperties().Where(p => p.CanRead);
foreach (var propertyInfo in properties)
{
MethodInfo getterMethodInfo = propertyInfo.GetGetMethod();
ParameterExpression entity = Expression.Parameter(typeof(TType));
MethodCallExpression getterCall = Expression.Call(entity, getterMethodInfo);
UnaryExpression castToObject = Expression.Convert(getterCall, typeof(object));
LambdaExpression lambda = Expression.Lambda(castToObject, entity);
var functionThatGetsValue = (Func<TType, object>)lambda.Compile();
}
After hours of googling found the answer here. I've added the snippets from the blog post as it might help others having the same troubles:
public static class PropertyInfoExtensions
{
public static Func<T, object> GetValueGetter<T>(this PropertyInfo propertyInfo)
{
if (typeof(T) != propertyInfo.DeclaringType)
{
throw new ArgumentException();
}
var instance = Expression.Parameter(propertyInfo.DeclaringType, "i");
var property = Expression.Property(instance, propertyInfo);
var convert = Expression.TypeAs(property, typeof(object));
return (Func<T, object>)Expression.Lambda(convert, instance).Compile();
}
public static Action<T, object> GetValueSetter<T>(this PropertyInfo propertyInfo)
{
if (typeof(T) != propertyInfo.DeclaringType)
{
throw new ArgumentException();
}
var instance = Expression.Parameter(propertyInfo.DeclaringType, "i");
var argument = Expression.Parameter(typeof(object), "a");
var setterCall = Expression.Call(
instance,
propertyInfo.GetSetMethod(),
Expression.Convert(argument, propertyInfo.PropertyType));
return (Action<T, object>)Expression.Lambda(setterCall, instance, argument).Compile();
}
}
I've modified gsharp's post above to actually set the value directly and make it a bit easier to use. It's not ideal as there is the introduction of the DynamicCast function which requires you to know your type up front. My goal was to try to keep us strongly typed and not return object and avoid dynamic keyword. Also, keep "magic" to a minimum.
public static T DynamicCast<T>(this object value)
{
return (T) value;
}
public static object GetPropertyValue<T>(this PropertyInfo propertyInfo, T objectInstance)
{
if (typeof(T) != propertyInfo.DeclaringType)
{
throw new ArgumentException();
}
var instance = Expression.Parameter(propertyInfo.DeclaringType, "i");
var property = Expression.Property(instance, propertyInfo);
var convert = Expression.TypeAs(property, propertyInfo.PropertyType);
var lambda = Expression.Lambda(convert, instance).Compile();
var result = lambda.DynamicInvoke(objectInstance);
return result;
}
public static void SetPropertyValue<T, TP>(this PropertyInfo propertyInfo, T objectInstance, TP value)
where T : class
where TP : class
{
if (typeof(T) != propertyInfo.DeclaringType)
{
throw new ArgumentException();
}
var instance = Expression.Parameter(propertyInfo.DeclaringType, "i");
var argument = Expression.Parameter(propertyInfo.PropertyType, "a");
var setterCall = Expression.Call(
instance,
propertyInfo.GetSetMethod(),
Expression.Convert(argument, propertyInfo.PropertyType));
var lambda = Expression.Lambda(setterCall, instance, argument).Compile();
lambda.DynamicInvoke(objectInstance, value);
}
Examples:
public void Get_Value_Of_Property()
{
var testObject = new ReflectedType
{
AReferenceType_No_Attributes = new object(),
Int32WithRange1_10 = 5,
String_Requires = "Test String"
};
var result = testObject.GetType().GetProperty("String_Requires").GetPropertyValue(testObject).DynamicCast<string>();
result.Should().Be(testObject.String_Requires);
}
public void Set_Value_Of_Property()
{
var testObject = new ReflectedType
{
AReferenceType_No_Attributes = new object(),
Int32WithRange1_10 = 5,
String_Requires = "Test String"
};
testObject.GetType().GetProperty("String_Requires").SetPropertyValue(testObject, "MAGIC");
testObject.String_Requires.Should().Be("MAGIC");
}
You could write a helper method which uses MakeGenericMethod or an expression tree to make a lambda do the typed call to call DynamicCast based on the PropertyInfo object and avoid having to know it up front. But that is less elegant.

Categories

Resources