Reputation: 3253
Essentially I'm trying to do this using expression trees
var properties = new Dictionary<string, object>();
foreach (var propInfo in objType.GetTypeInfo().GetProperties(BindingFlags.Public))
{
var name = propInfo.Name;
var value = propInfo.GetValue(objInstance);
properties.Add(name, value);
}
return properties;
I.e. create a dictionary of name and value pairs where name is the name of a property for objType
and value is the value of the property for the instance objInstance
of objType
Now converting this to an expression should compile to a delegate that simply does
Func<T, Dictionary<string, object>> func = i =>
{
var properties = new Dictionary<string, object>();
properties.Add("Prop1", (object)i.Prop1);
properties.Add("Prop2", (object)i.Prop2);
properties.Add("Prop3", (object)i.Prop3);
// depending upon the number of properties of T, Add will continue
return properties;
};
I know how to perform some of this, but what I am not sure on is how to create a local instance of a dictionary and then use it (and return it) in subsequent expressions?
Upvotes: 6
Views: 3530
Reputation: 1140
You only need Expressions to cache the getters, unless you know an easier way (e.g. maybe dynamic somehow?)
public static Func<T, Dictionary<string, object>> GetToDict<T>(){
var getters = typeof(T)
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Select(p => {
var param = Expression.Parameter(typeof(T));
return new KeyValuePair<PropertyInfo, Func<T, object>>(p,
Expression.Lambda<Func<T, object>>(Expression.Convert(Expression.MakeMemberAccess(param, p), typeof(object)), param).Compile());
})
.ToList();
return i => getters.ToDictionary(x => x.Key.Name, x => x.Value(i));
}
Upvotes: 1
Reputation: 111910
It should be something like (comments inline):
public static Func<T, Dictionary<string, object>> GetValuesFunc<T>()
{
Type objType = typeof(T);
var dict = Expression.Variable(typeof(Dictionary<string, object>));
var par = Expression.Parameter(typeof(T), "obj");
var add = typeof(Dictionary<string, object>).GetMethod("Add", BindingFlags.Public | BindingFlags.Instance, null, new[] { typeof(string), typeof(object) }, null);
var body = new List<Expression>();
body.Add(Expression.Assign(dict, Expression.New(typeof(Dictionary<string, object>))));
var properties = objType.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.Instance);
for (int i = 0; i < properties.Length; i++)
{
// Skip write only or indexers
if (!properties[i].CanRead || properties[i].GetIndexParameters().Length != 0)
{
continue;
}
var key = Expression.Constant(properties[i].Name);
var value = Expression.Property(par, properties[i]);
// Boxing must be done manually... For reference type it isn't a problem casting to object
var valueAsObject = Expression.Convert(value, typeof(object));
body.Add(Expression.Call(dict, add, key, valueAsObject));
}
// Return value
body.Add(dict);
var block = Expression.Block(new[] { dict }, body);
var lambda = Expression.Lambda<Func<T, Dictionary<string, object>>>(block, par);
return lambda.Compile();
}
use it like:
public class Test
{
public int A { get; set; }
public string B { get; set; }
}
and
Func<Test, Dictionary<string, object>> fn = GetValuesFunc<Test>();
var obj = new Test
{
A = 5,
B = "Foo"
};
var res = fn(obj);
Upvotes: 6