I want to get the Get Acessor of a Property (PropertyInfo) and compile it to a Func<object,object>. The declaring type is only known at runtime.
My current code is:
public Func<Object, Object> CompilePropGetter(PropertyInfo info)
{
MethodInfo getter = info.GetGetMethod();
ParameterExpression instance = Expression.Parameter(info.DeclaringType, info.DeclaringType.Name);
MethodCallExpression setterCall = Expression.Call(instance, getter);
Expression getvalueExp = Expression.Lambda(setterCall, instance);
Expression<Func<object, object>> GetPropertyValue = (Expression<Func<object, object>>)getvalueExp;
return GetPropertyValue.Compile();
}
Unfortunately, I have to put <Object,Object> as generic parameters, because sometimes I will get the properties of a Type, like typeof(T).GetProperties()[0].GetProperties(), where the first GetProperties()[] returns a custom-type object, and I have to reflect it.
When I run the code above, I get this error:
Unable to cast object of type 'System.Linq.Expressions.Expression`1[System.Func`2[**CustomType**,**OtherCustomType**]]' to type 'System.Linq.Expressions.Expression`1[System.Func`2[System.Object,System.Object]]'.
So, what can I do to return a Func<Object,Object>?
You can add casts to the expected type and from the return type using Expression.Convert:
public static Func<Object, Object> CompilePropGetter(PropertyInfo info)
{
ParameterExpression instance = Expression.Parameter(typeof(object));
var propExpr = Expression.Property(Expression.Convert(instance, info.DeclaringType), info);
var castExpr = Expression.Convert(propExpr, typeof(object));
var body = Expression.Lambda<Func<object, object>>(castExpr, instance);
return body.Compile();
}
Related
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.
I have a compiledLambda function that runs against an interface. Unfortunately the interface is just a marker interface and the real type is generated on the fly during runtime and has properties I want to do a grouping on.
Here's some sample code:
class Program
{
static void Main(string[] args)
{
// Just an example assignment: In the real life scenario the dynamic generated class is created during runtime.
IEnumerable<IDynamicGeneratedModelClass> list = GetDataFromService();
// get the 'real' type from the list
LambdaExpression lambdaExpression = DynamicExpression.ParseLambda(list.First().GetType(), typeof(object), "SomeProperty");
Func<IDynamicGeneratedModelClass, object> compiledLambdaFunction = (Func<IDynamicGeneratedModelClass, object>)lambdaExpression.Compile();
// Expected result: Group list on "SomeProp"
var result = list.GroupBy(compiledLambdaFunction);
}
private static IList<IDynamicGeneratedModelClass> GetDataFromService()
{
return new List<IDynamicGeneratedModelClass> {
new DynamicGeneratedModelClass("Class1"),
new DynamicGeneratedModelClass("Class2")
};
}
}
public interface IDynamicGeneratedModelClass
{}
public class DynamicGeneratedModelClass : IDynamicGeneratedModelClass
{
public DynamicGeneratedModelClass(string someProperty)
{
SomeProperty = someProperty;
}
public string SomeProperty { get; }
}
When the the lambda expression is compiled it throws the following exception:
System.InvalidCastException: 'Unable to cast object of type
'System.Func`2[ConsoleApp12.DynamicGeneratedModelClass,System.Object]'
to type
'System.Func`2[ConsoleApp12.IDynamicGeneratedModelClass,System.Object]'.'
Could you please give me a hint what I am doing wrong and how to fix it ?
The first generic parameter of the Func<T, TResult> delegate is declared as contravariant (in), which means that you can assign delegate with less derived parameter to delegate with more derived parameter, but not vice versa (in another words, you can cast Func<IDynamicGeneratedModelClass,Object> to Func<DynamicGeneratedModelClass,Object>, but cannot cast Func<DynamicGeneratedModelClass,Object> to Func<IDynamicGeneratedModelClass,Object>).
To avoid this problem, instead of lambda expression you generate now:
// lambda has "wrong" type Func<DynamicGeneratedModelClass, object>
(DynamicGeneratedModelClass item) => item.SomeProperty
generate lambda equivalent to this:
// lambda now has "correct" type Func<IDynamicGeneratedModelClass, object>
(IDynamicGeneratedModelClass item) => ((DynamicGeneratedModelClass)item).SomeProperty
I'm not familiar with the DynamicExpression library you used to generate you lambda, but this can be easily done using just System.Linq.Expression classes:
var itemType = list.First().GetType();
var propertyName = "SomeProperty";
var parameterExpr = Expression.Parameter(typeof(IDynamicGeneratedModelClass));
var castExpr = Expression.Convert(parameterExpr, itemType);
var propExpr = Expression.Property(castExpr, propertyName);
var lambdaExpr = Expression.Lambda(propExpr, parameterExpr);
// Compiled lambda is now of type Func<IDynamicGeneratedModelClass, object>
Func<IDynamicGeneratedModelClass, object> compiledLambdaFunction = (Func<IDynamicGeneratedModelClass, object>)lambdaExpr.Compile();
var result = list.GroupBy(compiledLambdaFunction);
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.
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!
I have a generic class like below:
public class MyClass<T, TProperty>
{
MyClass(Expression<Func<T, TProperty>> expression)
{
}
}
In my case, I want to dynamically create an instance of that class using Activator. So what I need is to create an expression for the constructor. What I have are the types (System.Type) and an object (System.Object) and I want something like below:
Expression<Func<T, TProperty>> exp = x => someValue;
"someValue" is declared as object but it's real type is definitely TProperty. It's resolved by reflection so the type here is object.
The problem is that the type T and TProperty will be generic, I don't know the types until run-time so I can't cast "someValue" to TProperty. What we have are typeof(T), typeof(TProperty) and an object -,-
Ok, I think I understand the question.
Given sample input:
Type typeOfT = typeof(int);
Type typeOfTProperty = typeof(string);
object someValue = "Test";
You want to create code equivalent to:
var myClassInstance = new MyClass<int, string>(t => "Test");
Here's how you can do that.
First, create the expression-tree:
var parameter = Expression.Parameter(typeOfT, "t");
var body = Expression.Constant(someValue, typeOfTProperty);
// Will automatically be an Expression<Func<T, TProperty>> without any extra effort.
var lambda = Expression.Lambda(body, parameter);
And then use reflection to create the MyClass<T, TProperty> instance:
var myClassType = typeof(MyClass<,>).MakeGenericType(typeOfT, typeOfTProperty);
// Make the MyClass<,> constructor public first...
var myClassInstance = Activator.CreateInstance(myClassType, lambda);