get and set object property using Expression trees instead of Reflection - c#

I would like to dynamically get and set an objects properties as follows:
public class Person
{
public string Name {get; set; }
}
public class Testing
{
public void Run()
{
var p = new Person();
SetValue(p, "Name", "Henry");
var name = GetValue(p, "Name");
}
}
Please could I get help creating the GetValue and SetValue methods using dynamic method (or expression trees)?
I am intending to save compiled expressions in a dictionary, to speed up future get/set calls.

Do you really want to use expression trees? for this simple scenario I would try to compile directly into a DynamicMethod using Reflection.Emit API's by getting an IL Generator. But .. for expression trees, i wrote a helper for you:
public class PropertyManager : DynamicObject
{
private static Dictionary<Type, Dictionary<string, GetterAndSetter>> _compiledProperties = new Dictionary<Type, Dictionary<string, GetterAndSetter>>();
private static Object _compiledPropertiesLockObject = new object();
private class GetterAndSetter
{
public Action<object, Object> Setter { get; set; }
public Func<Object, Object> Getter { get; set; }
}
private Object _object;
private Type _objectType;
private PropertyManager(Object o)
{
_object = o;
_objectType = o.GetType();
}
public static dynamic Wrap(Object o)
{
if (o == null)
return null; // null reference will be thrown
var type = o.GetType();
EnsurePropertySettersAndGettersForType(type);
return new PropertyManager(o) as dynamic;
}
private static void EnsurePropertySettersAndGettersForType(Type type)
{
if (false == _compiledProperties.ContainsKey(type))
lock (_compiledPropertiesLockObject)
if (false == _compiledProperties.ContainsKey(type))
{
Dictionary<string, GetterAndSetter> _getterAndSetters;
_compiledProperties[type] = _getterAndSetters = new Dictionary<string, GetterAndSetter>();
var properties = type.GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic);
foreach (var property in properties)
{
var getterAndSetter = new GetterAndSetter();
_getterAndSetters[property.Name] = getterAndSetter;
// burn getter and setter
if (property.CanRead)
{
// burn getter
var param = Expression.Parameter(typeof(object), "param");
Expression propExpression = Expression.Convert(Expression.Property(Expression.Convert(param, type), property), typeof(object));
var lambda = Expression.Lambda(propExpression, new[] { param });
var compiled = lambda.Compile() as Func<object, object>;
getterAndSetter.Getter = compiled;
}
if (property.CanWrite)
{
var thisParam = Expression.Parameter(typeof(Object), "this");
var theValue = Expression.Parameter(typeof(Object), "value");
var isValueType = property.PropertyType.IsClass == false && property.PropertyType.IsInterface == false;
Expression valueExpression;
if (isValueType)
valueExpression = Expression.Unbox(theValue, property.PropertyType);
else
valueExpression = Expression.Convert(theValue, property.PropertyType);
var thisExpression = Expression.Property (Expression.Convert(thisParam, type), property);
Expression body = Expression.Assign(thisExpression, valueExpression);
var block = Expression.Block(new[]
{
body,
Expression.Empty ()
});
var lambda = Expression.Lambda(block, thisParam, theValue);
getterAndSetter.Setter = lambda.Compile() as Action<Object, Object>;
}
}
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
var memberName = binder.Name;
result = null;
Dictionary<string, GetterAndSetter> dict;
GetterAndSetter getterAndSetter;
if (false == _compiledProperties.TryGetValue(_objectType, out dict)
|| false == dict.TryGetValue(memberName, out getterAndSetter))
{
return false;
}
if (getterAndSetter.Getter == null)
throw new NotSupportedException("The property has no getter!");
result = getterAndSetter.Getter(_object);
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
var memberName = binder.Name;
Dictionary<string, GetterAndSetter> dict;
GetterAndSetter getterAndSetter;
if (false == _compiledProperties.TryGetValue(_objectType, out dict)
|| false == dict.TryGetValue(memberName, out getterAndSetter))
{
return false;
}
if (getterAndSetter.Setter == null)
throw new NotSupportedException("The property has no getter!");
getterAndSetter.Setter(_object, value);
return true;
}
}
And this is how you can use it:
Person p = new Person();
p.Name = "mama";
var wrapped = PropertyManager.Wrap(p);
var val = wrapped.Name; // here we are using our compiled method ...
It is very obvious that you can extract my compilation logic to use strings instead of letting DLR giving the property name for you :)

Related

Make delegate for method at runtime

I found this interesting article Reflection Performance - Create Delegate (Properties C#)
the described approach works great for properties. So I tried to to make it work for Methods, too, but without success.
Classes / Properties / Methods
public class bmecatContent
{
private bmecatHeader header;
private bmecatCatalog catalog;
private List<bmecatFieldValue> fieldValueList;
public bmecatContent()
{
header = new bmecatHeader();
catalog = new bmecatCatalog();
}
public string DeclarationVersion { get; set; }
public string DeclarationEncoding { get; set; }
public string BmecatVersion { get; set; }
public bmecatHeader Header
{ get { return header; } }
public bmecatCatalog Catalog
{ get { return catalog; } }
}
public class bmecatCatalog
{
private List<bmecatCatalogGroupSystem> catalogGroupSystem;
private List<bmecatClassificationSystem> classificationSystem;
private List<bmecatProduct> products;
private List<bmecatProductToCataloggroupMap> productToCataloggroupMap;
public bmecatCatalog()
{
catalogGroupSystem = new List<bmecatCatalogGroupSystem>();
classificationSystem = new List<bmecatClassificationSystem>();
products = new List<bmecatProduct>();
productToCataloggroupMap = new List<bmecatProductToCataloggroupMap>();
}
public List<bmecatClassificationSystem> Classification_System
{ get { return classificationSystem; } }
public List<bmecatCatalogGroupSystem> Catalog_Group_System
{ get { return catalogGroupSystem; } }
public List<bmecatProduct> Products
{ get { return products; } }
public List<bmecatProductToCataloggroupMap> Product_To_Cataloggroup_Map
{ get { return productToCataloggroupMap; } }
public bmecatProduct GetProductByInernationalPid(string Pid)
{
// linq
var query = from prodItem in products
from innerList in prodItem.Product_Details.International_PID
where innerList.PID == Pid
select prodItem;
return query.FirstOrDefault();
}
}
my current Approach looks like:
// Properties
public static Func<object, object> BuildGetAccessor(MethodInfo method)
{
var obj = Expression.Parameter(typeof(object), "o");
Expression<Func<object, object>> expr =
Expression.Lambda<Func<object, object>>(
Expression.Convert(
Expression.Call(
Expression.Convert(obj, method.DeclaringType),
method),
typeof(object)),
obj);
return expr.Compile();
}
// Methods (with string Parameter)
public static Func<object, string, object> BuildMethodAccessor(MethodInfo method)
{
var obj = Expression.Parameter(typeof(object), "o");
var strParam = Expression.Parameter(typeof(string), "strParam");
//var param = method.GetParameters().Select(p => Expression.Parameter(p.ParameterType, p.Name)).FirstOrDefault();
var param = Expression.Convert(strParam, method.GetParameters().First().ParameterType);
Expression<Func<object, string, object>> expr =
Expression.Lambda<Func<object, string, object>>(
Expression.Convert(Expression.Call(Expression.Convert(obj, method.DeclaringType), method, param),
typeof(object)),
obj);
return expr.Compile();
}
this code generates messages, that for the lambda-declaration a wrong number of Parameters was used.
thx a lot for your help!
// Update
this is my "work in progress" part when it Comes to creating & using the delegates:
bmecatParser parser = new bmecatParser();
// parser contains Property BmecatContent of type bmecatContent
// BmecatContent contains all properties and Methods I Need to Access at runtime
// e.g. BmecatContent.Catalog, BmecatContent.Catalog.GetProductByInernationalPid(string Pid)
// gets instance of main-class
var property = parser.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Single(obj => obj.Name == "BmecatContent");
var access = Extensions.BuildGetAccessor(property.GetGetMethod());
var resultBmecatContent = access(parser);
// gets instance of class that holds method
property = resultBmecatContent.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Single(obj => obj.Name == "Catalog");
access = Extensions.BuildGetAccessor(property.GetGetMethod());
var resultCatalog = access(resultBmecatContent);
// here I try to get value from method that has 1 Parameter (string)
var method = resultCatalog.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance).Single(obj => obj.Name == "GetProductByInernationalPid");
var accessProd = Extensions.BuildMethodAccessor(method);
var resultProduct = accessProd(resultCatalog, "4317784548366");
the idea behind this is to parse given classes + properties structure, where user provides propertynames / methodnames within mappinginstructions.

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"); }
}
}

Expression for read from property and write to private readonly field

It is a long story ): I have some types look like this:
public class Model {
private readonly SomeType _member;
private readonly AnotherType _member2;
public Model(SomeType member, AnotherType member2) {
_member = member;
_member2 = member2;
}
public SomeType Member { get { return _member; } }
public AnotherType Member2 { get { return _member2; } }
}
I'm trying to create some expressions to create an instance of class, reads properties from some other objects (anon objects usually), and write the values to created instance's private fields -based on shown naming convention: Prop has field name: _prop.
I mean I want to write below objects to a new instance of Model:
var anon1 = new { Member = "something" };
// expected: new Model with _member = "something"
var anon2 = new { Member2 = "something" };
// expected: new Model with _member2 = "something"
var anon3 = new { Member = "something", Member2 = "something else" };
// expected: new Model with _member = "something" and _member2 = "something else"
I have created below code, it creates new instances, but do nothing with fields. Can you help me to find where I did wrong please?
public class InstanceCreator {
static public readonly Func<FieldInfo, PropertyInfo, bool> NamingConvention;
static InstanceCreator() {
NamingConvention = (f, p) => {
var startsWithUnderscope = f.Name.StartsWith("_");
var hasSameName = p.Name.Equals(f.Name.Remove(0, 1), StringComparison.OrdinalIgnoreCase);
var hasSameType = f.FieldType == p.PropertyType;
return startsWithUnderscope && hasSameName && hasSameType &&
f.IsInitOnly && !p.CanWrite;
};
}
private readonly Type _type;
private readonly Func<dynamic, dynamic> _creator;
public InstanceCreator(Type type) {
_type = type;
var ctor = GetCtor(type);
var propertyToFieldWriters = MakeWriters(type);
_creator = MakeCreator(type, ctor, propertyToFieldWriters);
}
private Expression GetCtor(Type type) {
if (type == typeof(string)) // ctor for string
return Expression.Lambda<Func<dynamic>>(
Expression.Constant(string.Empty));
if (type.IsValueType || // type has a parameterless ctor
type.GetConstructor(Type.EmptyTypes) != null)
return Expression.Lambda<Func<dynamic>>(Expression.New(type));
var info = typeof(FormatterServices).GetMethod("GetUninitializedObject");
var call = Expression.Call(info, Expression.Constant(type));
return call;
//return Expression.Lambda<Func<dynamic>>(call);
}
private IEnumerable<PropertyToFieldMapper> MakeWriters(Type type) {
var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
var list = (from field in fields
let property = properties.FirstOrDefault(prop => NamingConvention(field, prop))
where property != null
select new PropertyToFieldMapper(field, property)).ToList();
foreach (var item in list) {
var sourceParameter = Expression.Parameter(type, "sourceParameter");
var propertyGetter = Expression.Property(sourceParameter, item.Property.Name);
var targetParameter = Expression.Parameter(type, "targetParameter");
var setterInfo = item.Field.GetType().GetMethod("SetValue", new[] { typeof(object), typeof(object) });
var setterCall = Expression.Call(Expression.Constant(item.Field), setterInfo,
new Expression[] {
Expression.Convert(targetParameter,typeof(object)),
Expression.Convert(propertyGetter,typeof(object))
});
var call = Expression.Lambda(setterCall, new[] { targetParameter, sourceParameter });
item.Call = call;
}
return list;
}
private Func<dynamic, dynamic> MakeCreator(
Type type, Expression ctor,
IEnumerable<PropertyToFieldMapper> writers) {
var list = new List<Expression>();
// creating new target
var targetVariable = Expression.Variable(type, "targetVariable");
list.Add(Expression.Assign(targetVariable, Expression.Convert(ctor, type)));
// find all properties in incoming data
var sourceParameter = Expression.Parameter(typeof(object), "sourceParameter");
var sourceTypeVariable = Expression.Variable(typeof(Type));
var sourceTypeGetter = Expression.Call(sourceParameter, "GetType", Type.EmptyTypes);
list.Add(Expression.Assign(sourceTypeVariable, sourceTypeGetter));
var sourcePropertiesVariable = Expression.Variable(typeof(PropertyInfo[]));
var sourcePropertiesGetter = Expression.Call(sourceTypeVariable, "GetProperties", Type.EmptyTypes);
list.Add(Expression.Assign(sourcePropertiesVariable, sourcePropertiesGetter));
// itrate over writers and add their Call to block
foreach (var writer in writers) {
var param = Expression.Parameter(typeof(PropertyInfo));
var prop = Expression.Property(param, "Name");
var eq = Expression.Equal(Expression.Constant(writer.Property.Name), prop);
var any = CallAny.Call(sourcePropertiesVariable, Expression.Lambda(eq, param));
var predicate = Expression.IfThen(any,
Expression.Lambda(writer.Call, new[] { targetVariable, sourceParameter }));
list.Add(predicate);
}
list.Add(targetVariable);
var block = Expression.Block(new[] { targetVariable, sourceTypeVariable, sourcePropertiesVariable }, list);
var lambda = Expression.Lambda<Func<dynamic, dynamic>>(
block, new[] { sourceParameter }
);
return lambda.Compile();
}
public dynamic Create(dynamic data) {
return _creator.Invoke(data);
}
private class PropertyToFieldMapper {
private readonly FieldInfo _field;
private readonly PropertyInfo _property;
public PropertyToFieldMapper(FieldInfo field, PropertyInfo property) {
_field = field;
_property = property;
}
public FieldInfo Field {
get { return _field; }
}
public PropertyInfo Property {
get { return _property; }
}
public Expression Call { get; set; }
}
}
Also, I have this CallAny class, created from here.
public class CallAny {
public static Expression Call(Expression collection, Expression predicate) {
Type cType = GetIEnumerableImpl(collection.Type);
collection = Expression.Convert(collection, cType);
Type elemType = cType.GetGenericArguments()[0];
Type predType = typeof(Func<,>).MakeGenericType(elemType, typeof(bool));
// Enumerable.Any<T>(IEnumerable<T>, Func<T,bool>)
var anyMethod = (MethodInfo)
GetGenericMethod(typeof(Enumerable), "Any", new[] { elemType },
new[] { cType, predType }, BindingFlags.Static);
return Expression.Call(anyMethod, collection, predicate);
}
static MethodBase GetGenericMethod(Type type, string name, Type[] typeArgs, Type[] argTypes, BindingFlags flags) {
int typeArity = typeArgs.Length;
var methods = type.GetMethods()
.Where(m => m.Name == name)
.Where(m => m.GetGenericArguments().Length == typeArity)
.Select(m => m.MakeGenericMethod(typeArgs));
return Type.DefaultBinder.SelectMethod(flags, methods.ToArray(), argTypes, null);
}
static Type GetIEnumerableImpl(Type type) {
// Get IEnumerable implementation. Either type is IEnumerable<T> for some T,
// or it implements IEnumerable<T> for some T. We need to find the interface.
if (IsIEnumerable(type))
return type;
Type[] t = type.FindInterfaces((m, o) => IsIEnumerable(m), null);
Debug.Assert(t.Length == 1);
return t[0];
}
static bool IsIEnumerable(Type type) {
return type.IsGenericType
&& type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
}
}
And here is usage:
var inst = new InstanceCreator(typeof (Model)).Create(new {MyData});
Well, I found the problem. I should use ExpandoObject instead of dynamic keyword. And pass it to logic as IDictionary<string, object>. Here is the solution:
public class InstanceCreator {
static public readonly Func<FieldInfo, PropertyInfo, bool> NamingConvention;
static InstanceCreator() {
NamingConvention = (f, p) => {
var startsWithUnderscope = f.Name.StartsWith("_");
var hasSameName = p.Name.Equals(f.Name.Remove(0, 1), StringComparison.OrdinalIgnoreCase);
var hasSameType = f.FieldType == p.PropertyType;
return startsWithUnderscope && hasSameName && hasSameType &&
f.IsInitOnly && !p.CanWrite;
};
}
private readonly Type _type;
private readonly Func<IDictionary<string, object>, dynamic> _creator;
public InstanceCreator(Type type) {
_type = type;
var ctor = GetCtor(type);
var propertyToFieldMappers = MakeMappers(type);
_creator = MakeCreator(type, ctor, propertyToFieldMappers);
}
private Expression GetCtor(Type type) {
if (type == typeof(string)) // ctor for string
return Expression.Lambda<Func<dynamic>>(
Expression.Constant(string.Empty));
if (type.IsValueType || // type has a parameterless ctor
type.GetConstructor(Type.EmptyTypes) != null)
return Expression.Lambda<Func<dynamic>>(Expression.New(type));
var info = typeof(FormatterServices).GetMethod("GetUninitializedObject");
return Expression.Call(info, Expression.Constant(type));
}
private IEnumerable<PropertyToFieldMapper> MakeMappers(Type type) {
var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
var list = from field in fields
let property = properties.FirstOrDefault(prop => NamingConvention(field, prop))
where property != null
select new PropertyToFieldMapper(field, property);
return list;
}
private Func<IDictionary<string, object>, dynamic> MakeCreator(
Type type, Expression ctor,
IEnumerable<PropertyToFieldMapper> maps) {
var list = new List<Expression>();
var vList = new List<ParameterExpression>();
// creating new target
var targetVariable = Expression.Variable(type, "targetVariable");
vList.Add(targetVariable);
list.Add(Expression.Assign(targetVariable, Expression.Convert(ctor, type)));
// accessing source
var sourceType = typeof(IDictionary<string, object>);
var sourceParameter = Expression.Parameter(sourceType, "sourceParameter");
// calling source ContainsKey(string) method
var containsKeyMethodInfo = sourceType.GetMethod("ContainsKey", new[] { typeof(string) });
var accessSourceIndexerProp = sourceType.GetProperty("Item");
var accessSourceIndexerInfo = accessSourceIndexerProp.GetGetMethod();
// itrate over writers and add their Call to block
var containsKeyMethodArgument = Expression.Variable(typeof(string), "containsKeyMethodArgument");
vList.Add(containsKeyMethodArgument);
foreach (var map in maps) {
list.Add(Expression.Assign(containsKeyMethodArgument, Expression.Constant(map.Property.Name)));
var containsKeyMethodCall = Expression.Call(sourceParameter, containsKeyMethodInfo,
new Expression[] { containsKeyMethodArgument });
// creating writer
var sourceValue = Expression.Call(sourceParameter, accessSourceIndexerInfo,
new Expression[] { containsKeyMethodArgument });
var setterInfo = map.Field.GetType().GetMethod("SetValue", new[] { typeof(object), typeof(object) });
var setterCall = Expression.Call(Expression.Constant(map.Field), setterInfo,
new Expression[] {
Expression.Convert(targetVariable,typeof(object)),
Expression.Convert(sourceValue,typeof(object))
});
list.Add(Expression.IfThen(containsKeyMethodCall, setterCall));
}
list.Add(targetVariable);
var block = Expression.Block(vList, list);
var lambda = Expression.Lambda<Func<IDictionary<string, object>, dynamic>>(
block, new[] { sourceParameter }
);
return lambda.Compile();
}
public dynamic Create(IDictionary<string, object> data) {
return _creator.Invoke(data);
}
private class PropertyToFieldMapper {
private readonly FieldInfo _field;
private readonly PropertyInfo _property;
public PropertyToFieldMapper(FieldInfo field, PropertyInfo property) {
_field = field;
_property = property;
}
public FieldInfo Field {
get { return _field; }
}
public PropertyInfo Property {
get { return _property; }
}
}
}

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.

How do I set a field value in an C# Expression tree?

Given:
FieldInfo field = <some valid string field on type T>;
ParameterExpression targetExp = Expression.Parameter(typeof(T), "target");
ParameterExpression valueExp = Expression.Parameter(typeof(string), "value");
How do I compile a lambda expression to set the field on the "target" parameter to "value"?
.Net 4.0 : now that there's Expression.Assign, this is easy to do:
FieldInfo field = typeof(T).GetField("fieldName");
ParameterExpression targetExp = Expression.Parameter(typeof(T), "target");
ParameterExpression valueExp = Expression.Parameter(typeof(string), "value");
// Expression.Property can be used here as well
MemberExpression fieldExp = Expression.Field(targetExp, field);
BinaryExpression assignExp = Expression.Assign(fieldExp, valueExp);
var setter = Expression.Lambda<Action<T, string>>
(assignExp, targetExp, valueExp).Compile();
setter(subject, "new value");
.Net 3.5 : you can't, you'll have to use System.Reflection.Emit instead:
class Program
{
class MyObject
{
public int MyField;
}
static Action<T,TValue> MakeSetter<T,TValue>(FieldInfo field)
{
DynamicMethod m = new DynamicMethod(
"setter", typeof(void), new Type[] { typeof(T), typeof(TValue) }, typeof(Program));
ILGenerator cg = m.GetILGenerator();
// arg0.<field> = arg1
cg.Emit(OpCodes.Ldarg_0);
cg.Emit(OpCodes.Ldarg_1);
cg.Emit(OpCodes.Stfld, field);
cg.Emit(OpCodes.Ret);
return (Action<T,TValue>) m.CreateDelegate(typeof(Action<T,TValue>));
}
static void Main()
{
FieldInfo f = typeof(MyObject).GetField("MyField");
Action<MyObject,int> setter = MakeSetter<MyObject,int>(f);
var obj = new MyObject();
obj.MyField = 10;
setter(obj, 42);
Console.WriteLine(obj.MyField);
Console.ReadLine();
}
}
Setting a field is, as already discussed, problematic. You can can (in 3.5) a single method, such as a property-setter - but only indirectly. This gets much easier in 4.0, as discussed here. However, if you actually have properties (not fields), you can do a lot simply with Delegate.CreateDelegate:
using System;
using System.Reflection;
public class Foo
{
public int Bar { get; set; }
}
static class Program
{
static void Main()
{
MethodInfo method = typeof(Foo).GetProperty("Bar").GetSetMethod();
Action<Foo, int> setter = (Action<Foo, int>)
Delegate.CreateDelegate(typeof(Action<Foo, int>), method);
Foo foo = new Foo();
setter(foo, 12);
Console.WriteLine(foo.Bar);
}
}
private static Action<object, object> CreateSetAccessor(FieldInfo field)
{
DynamicMethod setMethod = new DynamicMethod(field.Name, typeof(void), new[] { typeof(object), typeof(object) });
ILGenerator generator = setMethod.GetILGenerator();
LocalBuilder local = generator.DeclareLocal(field.DeclaringType);
generator.Emit(OpCodes.Ldarg_0);
if (field.DeclaringType.IsValueType)
{
generator.Emit(OpCodes.Unbox_Any, field.DeclaringType);
generator.Emit(OpCodes.Stloc_0, local);
generator.Emit(OpCodes.Ldloca_S, local);
}
else
{
generator.Emit(OpCodes.Castclass, field.DeclaringType);
generator.Emit(OpCodes.Stloc_0, local);
generator.Emit(OpCodes.Ldloc_0, local);
}
generator.Emit(OpCodes.Ldarg_1);
if (field.FieldType.IsValueType)
{
generator.Emit(OpCodes.Unbox_Any, field.FieldType);
}
else
{
generator.Emit(OpCodes.Castclass, field.FieldType);
}
generator.Emit(OpCodes.Stfld, field);
generator.Emit(OpCodes.Ret);
return (Action<object, object>)setMethod.CreateDelegate(typeof(Action<object, object>));
}
Actually there is a way to set properties and fields with Expression Trees in .NET 3.5. It is may be the only option for some PCL profiles that do not support Delegate.CreateDelegate (besides the Reflection.Emit):
For field the trick is passing field as ref parameter,
e.g. SetField(ref holder.Field, "NewValue");
The property (as already pointed by Marc) can be set by reflecting and calling its setter method.
The full proof of concept is provided below as NUnit test fixture.
[TestFixture]
public class CanSetPropAndFieldWithExpressionTreeInNet35
{
class Holder
{
public int Field;
public string Prop { get; set; }
}
public static class FieldAndPropSetter
{
public static T SetField<T, TField>(T holder, ref TField field, TField value)
{
field = value;
return holder;
}
public static T SetProp<T>(T holder, Action<T> setProp)
{
setProp(holder);
return holder;
}
}
[Test]
public void Can_set_field_with_expression_tree_in_Net35()
{
// Shows how expression could look like:
Func<Holder, Holder> setHolderField = h => FieldAndPropSetter.SetField(h, ref h.Field, 111);
var holder = new Holder();
holder = setHolderField(holder);
Assert.AreEqual(111, holder.Field);
var holderType = typeof(Holder);
var field = holderType.GetField("Field");
var fieldSetterMethod =
typeof(FieldAndPropSetter).GetMethod("SetField")
.MakeGenericMethod(holderType, field.FieldType);
var holderParamExpr = Expression.Parameter(holderType, "h");
var fieldAccessExpr = Expression.Field(holderParamExpr, field);
// Result expression looks like: h => FieldAndPropSetter.SetField(h, ref h.Field, 222)
var setHolderFieldExpr = Expression.Lambda<Func<Holder, Holder>>(
Expression.Call(fieldSetterMethod, holderParamExpr, fieldAccessExpr, Expression.Constant(222)),
holderParamExpr);
var setHolderFieldGenerated = setHolderFieldExpr.Compile();
holder = setHolderFieldGenerated(holder);
Assert.AreEqual(222, holder.Field);
}
[Test]
public void Can_set_property_with_expression_tree_in_Net35()
{
// Shows how expression could look like:
Func<Holder, Holder> setHolderProp = h => FieldAndPropSetter.SetProp(h, _ => _.Prop = "ABC");
var holder = new Holder();
holder = setHolderProp(holder);
Assert.AreEqual("ABC", holder.Prop);
var holderType = typeof(Holder);
var prop = holderType.GetProperty("Prop");
var propSet = prop.GetSetMethod();
var holderParamExpr = Expression.Parameter(holderType, "h");
var callSetPropExpr = Expression.Call(holderParamExpr, propSet, Expression.Constant("XXX"));
var setPropActionExpr = Expression.Lambda(callSetPropExpr, holderParamExpr);
var propSetterMethod = typeof(FieldAndPropSetter).GetMethod("SetProp").MakeGenericMethod(holderType);
// Result expression looks like: h => FieldAndPropSetter.SetProp(h, _ => _.Prop = "XXX")
var setHolderPropExpr = Expression.Lambda<Func<Holder, Holder>>(
Expression.Call(propSetterMethod, holderParamExpr, setPropActionExpr),
holderParamExpr);
var setHolderPropGenerated = setHolderPropExpr.Compile();
holder = setHolderPropGenerated(holder);
Assert.AreEqual("XXX", holder.Prop);
}
}
I once made this class. Perhaps it helps:
public class GetterSetter<EntityType,propType>
{
private readonly Func<EntityType, propType> getter;
private readonly Action<EntityType, propType> setter;
private readonly string propertyName;
private readonly Expression<Func<EntityType, propType>> propertyNameExpression;
public EntityType Entity { get; set; }
public GetterSetter(EntityType entity, Expression<Func<EntityType, propType>> property_NameExpression)
{
Entity = entity;
propertyName = GetPropertyName(property_NameExpression);
propertyNameExpression = property_NameExpression;
//Create Getter
getter = propertyNameExpression.Compile();
// Create Setter()
MethodInfo method = typeof (EntityType).GetProperty(propertyName).GetSetMethod();
setter = (Action<EntityType, propType>)
Delegate.CreateDelegate(typeof(Action<EntityType, propType>), method);
}
public propType Value
{
get
{
return getter(Entity);
}
set
{
setter(Entity, value);
}
}
protected string GetPropertyName(LambdaExpression _propertyNameExpression)
{
var lambda = _propertyNameExpression as LambdaExpression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = lambda.Body as UnaryExpression;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = lambda.Body as MemberExpression;
}
var propertyInfo = memberExpression.Member as PropertyInfo;
return propertyInfo.Name;
}
test:
var gs = new GetterSetter<OnOffElement,bool>(new OnOffElement(), item => item.IsOn);
gs.Value = true;
var result = gs.Value;
Just for completeness here is the getter:
public static IEnumerable<Func<T, object>> GetTypeGetters<T>()
{
var fields = typeof (T).GetFields();
foreach (var field in fields)
{
ParameterExpression targetExp = Expression.Parameter(typeof(T), "target");
UnaryExpression boxedFieldExp = Expression.Convert(Expression.Field(targetExp, field), typeof(object));
yield return Expression.Lambda<Func<T,object>>(boxedFieldExp, targetExp).Compile();
}
}

Categories

Resources