Programmatically Create Collection of Property Accessor Functions - c#

What I want to do is take any class type and create a list of 'get' accessors to all of the properties in the object graph.
The exact format, order, etc of the collection doesn't matter, I just don't quite know how to start off identifying and creating accessors to all of the properties. It might take the form of something like this:
public static List<Func<T,object>> CreateAccessors<T>()
{
Type t = typeof(T);
// Identify all properties and properties of properties (etc.) of T
// Return list of lambda functions to access each one given an instance of T
}
public void MyTest()
{
MyClass object1;
var accessors = CreateAccessors<MyClass>();
var myVal1 = accessors[0](object1);
var myVal2 = accessors[1](object1);
// myVal1 might now contain the value of object1.Property1
// myVal2 might now contain the value of object1.Property4.ThirdValue.Alpha
}

You can use reflection to extract the properties and expression-trees to help build the delegates targeting the property getters:
// Start with all public instance properties of type
var accessors = from property in type.GetProperties
(BindingFlags.Instance | BindingFlags.Public)
// Property must be readable
where property.CanRead
//Assemble expression tree of the form:
// foo => (object) foo.Property
// foo
let parameter = Expression.Parameter(type, "foo")
// foo.Property
let propertyEx = Expression.Property(parameter, property)
// (object)foo.Property - We need this conversion
// because the property may be a value-type.
let body = Expression.Convert(propertyEx, typeof(object))
// foo => (object) foo.Property
let expr = Expression.Lambda<Func<T,object>>(body, parameter)
// Compile tree to Func<T,object> delegate
select expr.Compile();
return accessors.ToList();
Note that although Delegate.CreateDelegate seems like an obvious choice, you will have some problems boxing value-type properties. Expression-trees dodge this problem elegantly.
Note that you'll need some more work to be able to get "nested" properties out too, but hopefully I've given ypu enough to get you started (hint: recurse). One final pointer with that: watch out for cycles in the object graph!

public static List<Func<T, object>> CreateAccessors<T>()
{
var accessors = new List<Func<T, object>>();
Type t = typeof(T);
foreach (PropertyInfo prop in t.GetProperties(BindingFlags.Instance | BindingFlags.Public)) {
if (prop.CanRead) {
var p = prop;
accessors.Add(x => p.GetValue(x, null));
}
}
return accessors;
}
EDIT:
Here is a variant which returns compiled expressions and should be much faster than the previous one using reflection
public static List<Func<T, object>> CreateAccessorsCompiled<T>()
{
var accessors = new List<Func<T, object>>();
Type t = typeof(T);
foreach (PropertyInfo prop in t.GetProperties(BindingFlags.Instance | BindingFlags.Public)) {
if (prop.CanRead) {
ParameterExpression lambdaParam = Expression.Parameter(t, "instance");
Expression bodyExpression;
MemberExpression memberAccessExpression = Expression.MakeMemberAccess(Expression.Convert(lambdaParam, t), prop);
if (prop.PropertyType == typeof(object)) { // Create lambda expression: (instance) => ((T)instance).Member
bodyExpression = memberAccessExpression;
} else { // Create lambda expression: (instance) => (object)((T)instance).Member
bodyExpression = Expression.Convert(memberAccessExpression, typeof(object));
}
var lambda = Expression.Lambda<Func<T, object>>(bodyExpression, lambdaParam);
accessors.Add(lambda.Compile());
}
}
return accessors;
}

Related

C# LINQ OrderBy() Expression, Creating Nested Generic type

I have a class Person
class Person
{
string Name;
int Age;
DateTime BirthDate;
}
I am trying to create an Expression to order an IQueryable<Person> by a given property name.
public IQueryable<Person> Order(IQueryable<Person> input, string sortColumnName)
{
Type type = typeof(Person);
PropertyInfo propertyInfo = type.GetProperty(sortColumnName);
Type pType = prop.Type;
ParameterExpression param = Expression.Parameter(type, "y");
Expression prop = param;
prop = Expression.Property(prop, propertyInfo);
// I want to achieve..
// var orderExpression = Expression.Lambda<Func<Person, "pType">>(prop, param);
// In order to do something like the above statement,
// I have to create a nested generic type of `Func<Person, "pType">`
Type e1 = typeof(Expression<>);
Type[] typeArgs = {typeof( Func <Person, pType>)};
Type orderType = e1.MakeGenericType(typeArgs);
// Need some help of how to create and use this Generic ""orderType"".
// ....
// Ultimately, it will by used somewhat like ...
//
// var orderExpression = Expression.Lambda<"orderType">(prop, param);
return input.OrderBy(orderExpression);
}
I am really confused by this inconsistent behavior of Expression trees.
In my project, I have an ""IQueryable<Person>"".Where(w1) that can easily take in an Expression w1.
I am simply trying to do the same by creating an ""IQueryable<Person>"".OrderBy(o1),
to successfully consume an Expression o1.
I would prefer to keep everything as IQueryable<>, instead of having to convert back and forth to IEnumerable<>.
Any help would be appreciated!
I'll left common solution. Expression tree is a nightmare only from start.
public IQueryable<T> Order(this IQueryable<T> input, string sortColumnName)
{
Type type = typeof(T);
var propertyInfo = type.GetProperty(sortColumnName);
if (propertyInfo == null)
throw new InvalidOperationException();
var param = Expression.Parameter(type, "y");
var orderLambda = Expression.Lambda(
Expression.MakeMemberAccess(param, propertyInfo),
param);
var queryExpr = Expression.Call(
typeof(Queryable),
"OrderBy",
new Type[] {
input.ElementType,
propertyInfo.PropertyType },
input.Expression,
orderLambda);
return input.Provider.CreateQuery<T>(queryExpr);
}
Note, that I can make small mistake because just writing from memory.

Expression.Lambda can't convert types automatically

I have some properties in many classes and want to compile getter/setter of them into lambda expressions so that I can use reflections with better performance.
(Profiled, and using Reflection's getValue/setValue takes up about 78% of total running time...)
However It seems that Expression.Lambda() Only supports one collection of parameter and will not convert parameter type automatically.
using Exp = System.Linq.Expressions.Expression;
...
public class A { public int propA { get; set; } }
public class B { public int propB { get; set; } }
static Func<object, int> BuildFunc(Type type, string propName)
{
var param = Exp.Parameter(prop.DeclaringType, "x");
var exBody = Exp.Call(param, prop.GetGetMethod());
return Exp.Lambda<Func<object, int>>(exBody, param).Compile();
}
...
var a = new A();
var b = new B();
var fA = BuildFunc(typeof(A).GetProperty("propA"));
var fB = BuildFunc(typeof(B).GetProperty("propB"));
fA(a);
fB(b);
It will thorw an exception:
ParameterExpression of type __Main__+A cannot be used for delegate parameter of type System.Object
If I change the expression into Exp.Lambda<Func<A, int>>(...) it will work with class A but will not work with class B.
If I use Expression.Convert to convert types, it will throw ArgumentException that tells me the method cannot invoke on instance of System.Object.
So what can I do to compile this expression just like below, which supports any type of object and corresponding method?
lambda = (object obj, MethodInfo method, ...) => { method.Invoke(obj, ...) }
There are better expression APIs available for invoking property getter/setter methods. You definitely don't have to resort to MethodInfo.Invoke.
The conversion from/to object is handled by Expression.Convert. You just need to plug it in the right spot.
Here is a getter/setter delegate compilation example where T is the type of your container (A or B in your example).
static Func<T, object> CompileGetter<T>(PropertyInfo property)
{
// Target expression: (T obj) => (object)obj.Property;
ParameterExpression objParam = Expression.Parameter(typeof(T), "obj");
Expression body = Expression.Convert(Expression.MakeMemberAccess(objParam, property), typeof(object));
return Expression
.Lambda<Func<T, object>>(body, objParam)
.Compile();
}
static Action<T, object> CompileSetter<T>(PropertyInfo property)
{
// Target expression: (T obj, object value) => obj.Property = (TProperty)value;
ParameterExpression objParam = Expression.Parameter(typeof(T), "obj");
ParameterExpression valueParam = Expression.Parameter(typeof(object), "value");
Expression body = Expression.Assign(
Expression.MakeMemberAccess(objParam, property),
Expression.Convert(valueParam, property.PropertyType)
);
return Expression
.Lambda<Action<T, object>>(body, objParam, valueParam)
.Compile();
}
Proof:
class Dummy
{
public int Value { get; set; }
}
...
PropertyInfo prop = typeof(Dummy).GetProperty("Value");
Func<Dummy, object> getter = CompileGetter<Dummy>(prop);
Action<Dummy, object> setter = CompileSetter<Dummy>(prop);
Dummy d = new Dummy { Value = 123 };
Assert.AreEqual(123, getter(d));
setter(d, 321);
Assert.AreEqual(321, d.Value);
Please note: LambdaExpression.Compile is an extremely CPU-intensive operation, so once you create your getter/setter delegate, you must cache it in order to get that performance boost you're looking for.

Dynamic delegate convert to dynamic function

I'd like to copy an object into a mock of the same Interface. The goal is, to do it dynamic. But it seems, that there is no way to convert a dynamic delegate into a function.
public static T GetCopiedMock<T>(T toCopy, T mockObject) where T : class
{
IEnumerable<PropertyInfo> properties = GetPropertyInfos(toCopy, typeof(T));
foreach (var property in properties)
{
var parameter = Expression.Parameter(typeof(T));
var result = Expression.Property(parameter, property);
var lamda = Expression.Lambda(result, parameter);
var compilat = lamda.Compile();
var funcType = typeof(Func<,>).MakeGenericType(typeof(T), property.PropertyType);
//Here is my problem
mockObject.Expect(new Func<T, property.PropertyType>(compilat));
}
return mockObject;
}
I know its not possible directly since propertytype is at runtime but is there is any workaround?
By the way, this is my first post. So if you see something terrible which i have to do better, tell me!

Using refection to get all properties and pass each property in method

I'm using simplemvvmtoolkit for validation (INotifyDataErrorInfo). Instead of me repeating my self over and over for each property within the view model, Id like to use reflection to get all the properties and validate them, I can't seem to figure out what to pass in the validateProperty method though.
private void ValidateInput()
{
var unitProperties = this.GetType().GetProperties()
.Where(x => x.CanRead);
foreach (var prop in unitProperties)
ValidateProperty(prop, prop.GetValue(this, null)); //????
//? ^ get errors here
}
ValidateProperty takes in:
protected virtual void ValidateProperty<TResult>(Expression<Func<TViewModel, TResult>> property, object value);
The problem is Expression<Func<TViewModel, TResult>> has absolutely no relation to PropertyInfo (the type returned by GetProperties). You'll also run into problems because the type of the result is not known at compile time.
The easiest solution would be to change ValidateProperty to accept a PropertyInfo:
protected virtual void ValidateProperty(PropertyInfo property, object value);
You could also convert the PropertyInfo to an Expression, but that's a bit more difficult:
var method = this.GetType().GetMethod("ValidateProperty");
foreach (var prop in unitProperties)
{
var parameter = Expression.Parameter(this.GetType(), "_");
var property = Expression.Property(parameter, prop);
var lambda = Expression.Lambda(property, parameter);
var genericMethod = method.MakeGenericMethod(prop.PropertyType);
genericMethod.Invoke(this, new object[] { lambda, prop.GetValue(this, null) });
}

Matching an interface's ProperyInfo with a class's PropertyInfo

I use a method similar to the following to get some precomputed metadata related to a Type's properties.
MyData GetProperty<T, U>(Expression<Func<T, U>> member)
{
// Get the property referenced in the lambda expression
MemberExpression expression = member.Body as MemberExpression;
PropertyInfo property = expression.Member as PropertyInfo;
// get the properties in the type T
PropertyInfo[] candidates = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
// Find the match
foreach (PropertyInfo candidate in candidates)
if (candidate == property)
return GetMetaData<T>(candidate);
throw new Exception("Property not found.");
}
// Returns precomputed metadata
MyData GetMetaData<T>(PropertyInfo property) { ... }
As you would expect, it works when used as follows:
var data = PropertyInfo((Employee e) => e.Name);
But not when used in the following generic method:
void MyGenericMethod<T>(int id) where T : IEmployee
{
var data = PropertyInfo((T e) => e.Name);
}
It fails because the declaring type of property in the first method is now IEmployee, so the property in the lambda doesn't match the property in the type. How can I get them to match, without relying on the names of the properties? (There can be multiple properties with the same name if interfaces are implemented explicitly, so p1.Name == p2.Name won't cut it).
What you'd probably need is an InterfaceMapping. You can get that from the actual type by calling GetInterfaceMap(typeof(interface)), i.e.,
InterfaceMapping mapping = typeof(Employee).GetInterfaceMap(typeof(IEmployee));
Now, the mapping will contain the fields InterfaceMethods which will contain the methods you see when reflecting the interface, and TargetMethods which are the class's implementing methods. Note that this maps the the getter methods from the interface to the getter methods from the target class. You'll need to find the proper interface property by mapping the getter method of the various properties of the class to the found getter method.
Type interfaceType = typeof(IEmployee);
Type classType = typeof(Employee);
PropertyInfo nameProperty = interfaceType.GetProperty("Name");
MethodInfo nameGetter = nameProperty.GetGetMethod();
InterfaceMapping mapping = classType.GetInterfaceMap(interfaceType);
MethodInfo targetMethod = null;
for (int i = 0; i < mapping.InterfaceMethods.Length; i++)
{
if (mapping.InterfaceMethods[i] == nameGetter)
{
targetMethod = mapping.TargetMethods[i];
break;
}
}
PropertyInfo targetProperty = null;
foreach (PropertyInfo property in classType.GetProperties(
BindingFlags.Instance | BindingFlags.GetProperty |
BindingFlags.Public | BindingFlags.NonPublic)) // include non-public!
{
if (targetMethod == property.GetGetMethod(true)) // include non-public!
{
targetProperty = property;
break;
}
}
// targetProperty is the actual property
Caution: Note the use of BindingFlags.NonPublic and GetGetMethod(true) here, for accessing private members. If you've got an explicit interface implementation, there isn't really a public property matching the interface's property, instead there is a private property named Some.NameSpace.IEmployee.Name that is mapped (which is, of course, your explicit implementation).
When you've found the right property, you can just call
ParameterExpression p = Expression.Parameter("e", typeof(T));
Expression<Func<T, U>> lambda = Expression.Lambda<Func<T, U>>(
Expression.Property(p, targetProperty), p);
and you've got yourself a lambda expression that uses the class's properties rather than the interface's properties.
Does BindingFlags.FlattenHierarchy work? If not, you could always iterate through typeof(T).GetInterfaces and call GetProperties on each of them.
You'll need to get the member name from the lambda expression, and use reflection to get that member off of the type you've been given:
public static PropertyInfo PropInfo<TContainer, TMember>(
Expression<Func<TContainer, TMember>> memberGetter)
{
var memberName = GetExpressionMemberName(memberGetter);
return typeof(TContainer).GetProperty(memberName);
}
public static string GetExpressionMemberName<TContainer, TMember>(
Expression<Func<TContainer, TMember>> memberGetter)
{
var expressionType = memberGetter.Body.NodeType;
switch (expressionType)
{
case ExpressionType.MemberAccess:
{
var memberExpr = (MemberExpression) memberGetter.Body;
return memberExpr.Member.Name;
}
case ExpressionType.Convert:
{
var convertExpr = (UnaryExpression) memberGetter.Body;
var memberExpr = (MemberExpression) convertExpr.Operand;
return memberExpr.Member.Name;
}
default:
throw new InvalidOperationException("Expression {0} does not represent a simple member access.");
}
}
Here's proof that it works:
void Main()
{
Console.WriteLine(
MyGenericMethod<Employee>()
.GetGetMethod()
.Invoke(
new Employee {Name = "Bill"},
new object[] {}));
}
public class Employee : IEmployee {
public string Name {get;set;}
string IEmployee.Name { get { throw new Exception(); } }
}
public interface IEmployee {string Name {get;}}
public PropertyInfo MyGenericMethod<T>() where T : IEmployee
{
return PropInfo((T e) => e.Name);
}
Console output:
Bill

Categories