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; }
}
}
}
Related
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 :)
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"); }
}
}
I have a class with a few properties, I've got a custom attribute setup, one for TextField and one for ValueField, I am using an IEnumerable, so can't just select the fields I want, I need to basically:
collectionItems.ToDictionary(o => o.FieldWithAttribute<TextField>, o => o.FieldWithAttribute<ValueField>);
Hopefully you get what I am trying to do, this doesn't need to use generics as above, I just need to do something similar to get the marked fields from a large object, so I can have a nice little dictionary of key value pairs.
Example class that is the TEntity:
public class Product
{
[TextField]
public string ProductTitle { get; set; }
[ValueField]
public int StyleID { get; set; }
//Other fields...
}
Any ideas how I can achieve this? Perhaps using reflection somehow in the LINQ statement?
If you're going to use reflection, you should probably cache the member accessors to avoid the performance hit of reflecting on every item, every time. You could do something like this:
// Type aliases used for brevity
using Accessor = System.Func<object, object>;
using E = System.Linq.Expressions.Expression;
internal static class AttributeHelpers
{
private const BindingFlags DeclaredFlags = BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.DeclaredOnly;
private const BindingFlags InheritedFlags = BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic;
private static readonly Accessor NullCallback = _ => null;
[ThreadStatic]
private static Dictionary<Type, Dictionary<Type, Accessor>> _cache;
private static Dictionary<Type, Accessor> GetCache<TAttribute>()
where TAttribute : Attribute
{
if (_cache == null)
_cache = new Dictionary<Type, Dictionary<Type, Accessor>>();
Dictionary<Type, Accessor> cache;
if (_cache.TryGetValue(typeof(TAttribute), out cache))
return cache;
cache = new Dictionary<Type, Accessor>();
_cache[typeof(TAttribute)] = cache;
return cache;
}
public static object MemberWithAttribute<TAttribute>(this object target)
where TAttribute : Attribute
{
if (target == null)
return null;
var accessor = GetAccessor<TAttribute>(target.GetType());
if (accessor != null)
return accessor(target);
return null;
}
private static Accessor GetAccessor<TAttribute>(Type targetType)
where TAttribute : Attribute
{
Accessor accessor;
var cache = GetCache<TAttribute>();
if (cache.TryGetValue(targetType, out accessor))
return accessor;
var member = FindMember<TAttribute>(targetType);
if (member == null)
{
cache[targetType] = NullCallback;
return NullCallback;
}
var targetParameter = E.Parameter(typeof(object), "target");
var accessorExpression = E.Lambda<Accessor>(
E.Convert(
E.MakeMemberAccess(
E.Convert(targetParameter, targetType),
member),
typeof(object)),
targetParameter);
accessor = accessorExpression.Compile();
cache[targetType] = accessor;
return accessor;
}
private static MemberInfo FindMember<TAttribute>(Type targetType)
where TAttribute : Attribute
{
foreach (var property in targetType.GetProperties(DeclaredFlags))
{
var attribute = property.GetCustomAttribute<TAttribute>();
if (attribute != null)
return property;
}
foreach (var field in targetType.GetFields(DeclaredFlags))
{
var attribute = field.GetCustomAttribute<TAttribute>();
if (attribute != null)
return field;
}
foreach (var property in targetType.GetProperties(InheritedFlags))
{
var attribute = property.GetCustomAttribute<TAttribute>();
if (attribute != null)
return property;
}
foreach (var field in targetType.GetFields(InheritedFlags))
{
var attribute = field.GetCustomAttribute<TAttribute>();
if (attribute != null)
return field;
}
return null;
}
}
It's up to you how you want to deal with items whose types lack the desired attributed members. I chose to return null.
Example usage:
var lookup = Enumerable
.Range(1, 20)
.Select(i => new Product { Title = "Product " + i, StyleID = i })
.Select(
o => new
{
Text = o.MemberWithAttribute<TextFieldAttribute>(),
Value = o.MemberWithAttribute<ValueFieldAttribute>()
})
.Where(o => o.Text != null && o.Value != null)
.ToDictionary(o => o.Text, o => o.Value);
foreach (var key in lookup.Keys)
Console.WriteLine("{0}: {1}", key, lookup[key]);
public static object FieldWithAttribute<T>(this object obj)
{
var field = obj.GetType()
.GetProperties()
.SIngleOrDefault(x => x.CustomAattributes.Any(y => y.AttributeType == typeof(T));
return field != null ? field.GetValue(obj) : null;
}
something like this
This should do the trick
public static TRet FieldWithAttribute<TAttr, TRet>(this object obj) where TAttr : Attribute
{
var field = obj.GetType()
.GetProperties()
.SingleOrDefault(x => Attribute.IsDefined(x, typeof (TAttr)));
return field == null ? default(TRet) : (TRet)field.GetValue(obj);
}
and when you use it
var dictionary = products.ToDictionary(x => x.FieldWithAttribute<TextFieldAttribute, string>(),
x => x.FieldWithAttribute<ValueFieldAttribute, int>());
I'm displaying a table of objects Company in a webpage and I am using a Dynamic Linq OrderBy to sort them on each property.
I'm using this code https://stackoverflow.com/a/233505/265122
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "OrderBy");
}
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "OrderByDescending");
}
public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "ThenBy");
}
public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "ThenByDescending");
}
static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName) {
string[] props = property.Split('.');
Type type = typeof(T);
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach(string prop in props) {
// use reflection (not ComponentModel) to mirror LINQ
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
object result = typeof(Queryable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof(T), type)
.Invoke(null, new object[] {source, lambda});
return (IOrderedQueryable<T>)result;
}
It's great but I would also like to sort the companies on the number of employees.
Like this : query.OrderBy("Employees.Count")
I tried to call to the Count method dynamically without any success so far.
I modified the code like this :
foreach(string prop in props)
{
if (prop == "Count")
{
var countMethod = (typeof(Enumerable)).GetMethods().First(m => m.Name == "Count").MakeGenericMethod(type);
expr = Expression.Call(countMethod, expr);
break;
}
// Use reflection (not ComponentModel) to mirror LINQ.
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
But I have an exception on the expr = Expression.Call(countMethod, expr);
The exception is:
ArgumentException
Expression of type 'System.Collections.Generic.ICollection`1[Employee]'
cannot be used for parameter of type
'System.Collections.Generic.IEnumerable`1[System.Collections.Generic.ICollection`1
[Employee]]' of method 'Int32 Count[ICollection`1]
System.Collections.Generic.IEnumerable`1[System.Collections.Generic.ICollection`1
Employee]])'
Any idea on how to achieve that?
From your gist below I have found a easy way t flatten the properties across all base types and interfaces as demonstrated on this post.
So I implemented the extension method for PropertyInfo that will return all properties from all interfaces and base classes inherited by the type. The problem was that IList does not have a Count property however iCollection does. The public static PropertyInfo[] GetPublicProperties(this Type type) will flat all properties and we get the correct one from there this should work on any property now not only Count.
public class Program
{
private static IList<Company> _companies;
static void Main(string[] args)
{
var sort = "Employees.Count";
_companies = new List<Company>();
_companies.Add(new Company
{
Name = "c2",
Address = new Address {PostalCode = "456"},
Employees = new List<Employee> {new Employee(), new Employee()}
});
_companies.Add(new Company
{
Name = "c1",
Address = new Address {PostalCode = "123"},
Employees = new List<Employee> { new Employee(), new Employee(), new Employee() }
});
//display companies
_companies.AsQueryable().OrderBy(sort).ToList().ForEach(c => Console.WriteLine(c.Name));
Console.ReadLine();
}
}
public class Company
{
public string Name { get; set; }
public Address Address { get; set; }
public IList<Employee> Employees { get; set; }
}
public class Employee{}
public class Address
{
public string PostalCode { get; set; }
}
public static class OrderByString
{
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "OrderBy");
}
public static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
{
string[] props = property.Split('.');
Type type = typeof(T);
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach (string prop in props)
{
// use reflection (not ComponentModel) to mirror LINQ
PropertyInfo pi = type.GetPublicProperties().FirstOrDefault(c => c.Name == prop);
if (pi != null)
{
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
else { throw new ArgumentNullException(); }
}
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
object result = typeof(Queryable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof(T), type)
.Invoke(null, new object[] { source, lambda });
return (IOrderedQueryable<T>)result;
}
public static PropertyInfo[] GetPublicProperties(this Type type)
{
if (type.IsInterface)
{
var propertyInfos = new List<PropertyInfo>();
var considered = new List<Type>();
var queue = new Queue<Type>();
considered.Add(type);
queue.Enqueue(type);
while (queue.Count > 0)
{
var subType = queue.Dequeue();
foreach (var subInterface in subType.GetInterfaces())
{
if (considered.Contains(subInterface)) continue;
considered.Add(subInterface);
queue.Enqueue(subInterface);
}
var typeProperties = subType.GetProperties(
BindingFlags.FlattenHierarchy
| BindingFlags.Public
| BindingFlags.Instance);
var newPropertyInfos = typeProperties
.Where(x => !propertyInfos.Contains(x));
propertyInfos.InsertRange(0, newPropertyInfos);
}
return propertyInfos.ToArray();
}
return type.GetProperties(BindingFlags.FlattenHierarchy
| BindingFlags.Public | BindingFlags.Instance);
}
}
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();
}
}