Expression to create instance and set members not using MemberInit - c#

I need to create an instance and set some properties. Normally I would use Expression.MemberInit(Expression.New(type), memberBindings), but I need to create the instance by calling a custom method instead. I can't figure out how to set members on the instance expression.
I'm creating the instance like this. I'm not actually using Activator.CreateInstance, but it should work for this example.
var method = typeof( Activator ).GetMethod( nameof( Activator.CreateInstance ), new Type[] { typeof( Type ) } );
Expression expression = Expression.Call( method, Expression.Constant( typeof( TestClass ) ) );
After I have the expression to create the instance, how can I set the objects members?

It's possible to create such expression with Expression.Block, but note that it can be used only to compile lambda delegate since the expression blocks are not supported by C# compiler and query providers other than LINQ to Objects.
Let first make sure the create instance method returns the correct type:
var method = typeof(Activator).GetMethod(nameof(Activator.CreateInstance), new Type[] { typeof(Type) });
var newObj = Expression.Convert(Expression.Call(method, Expression.Constant(typeof(TestClass))), typeof(TestClass));
and assuming you already have a list of MemberAssignment expressions which you'd normally use with Expression.MemberInit:
IEnumerable<MemberAssignment> memberBindings = ...;
Then the expression in question can be build as follows:
var obj = Expression.Variable(newObj.Type, "obj");
var expressions = new List<Expression>();
expressions.Add(Expression.Assign(obj, newObj));
expressions.AddRange(memberBindings.Select(b => Expression.Assign(
Expression.MakeMemberAccess(obj, b.Member), b.Expression)));
expressions.Add(obj);
var variables = new Expression[] { obj };
var result = Expression.Block(variables, expressions);
So we create a variable called obj, assign the result of the object creating method, then generate member assignments for each member binding, and finally return the obj (the last expression in the block expression list).

Related

How to return value in ExpressionTree

i'm trying to do for an expression tree and try to let it return a simple int value. but it not working anymore
var method = typeof(Console).GetMethod("WriteLine", new Type[] {typeof(string)});
var result = Expression.Variable(typeof(int));
var block = Expression.Block(
result,
Expression.Assign(result,Expression.Constant(0)),
Expression.IfThenElse(Expression.Constant(true),
Expression.Block(Expression.Call(null, method, Expression.Constant("True")),
Expression.Assign(result, Expression.Constant(1))),
Expression.Block(Expression.Call(null, method, Expression.Constant("False")), Expression.Assign(
result, Expression.Constant(0)
))),
result
);
Expression.Lambda<Func<int>>(block).Compile()();
The problem is not with returning a vaue from the block (you are doing it correctly), but the out of scope variable due to the used wrong Expression.Block method overload.
Variable expressions like your result must be passed to the block expression using some of the overloads with IEnumerable<ParameterExpression> variables argument, for instance
var block = Expression.Block(
new ParameterExpression[] { result },
//... (the rest of the sample code unchanged)
);

Lambda expression to initialize class with parameter and properties

I have a method that is used to generate a NewExpression.
public NewExpression CreateNewExpression( Expression<Func<T>> expression )
{
return expression.Body as NewExpression;
}
You can then do stuff like this.
CreateNewExpression( () => new MyType() );
This is later on used to create a delegate the can be executed to create an object.
var body = Expression.MemberInit( constructorExpression, bindings );
var funcType = typeof( Func<> ).MakeGenericType( objectType );
var lambda = Expression.Lambda( funcType, body ).Compile();
This works great, but now I would like to pass some data into the method so it can be used in the construction of the object.
public NewExpression CreateNewExpression( Expression<Func<T, Data>> expression )
This would then be used like this.
CreateNewExpression( data => new MyType( data.Id ) );
I can't for the life of me figure out how to create the proper expression to pass the data in for the constructor to use.
If I don't extract the body from the expression in CreateNewExpression and do Expression.Invoke, I don't know how to set the properties to it after.
If I extract the body, I can set the properties using Expression.MemberInit, but then I can't figure out how to pass the data to it.
How can I do this?
When you want to use an expression that contains ParameterExpression, you need to reuse that same ParameterExpression in the final expression too.
To do this, you can access the Parameters of the original expression and then pass the parameter to Expression.Lambda(). The result could look something like this:
public Delegate M<T>(Expression<Func<Data, T>> expression)
{
var objectType = typeof(T);
var constructorExpression = (NewExpression)expression.Body;
var constructorParameterExpression = expression.Parameters.Single();
var bindings = …;
var body = Expression.MemberInit( constructorExpression, bindings );
var funcType = typeof( Func<,> ).MakeGenericType( typeof(Data), objectType );
var lambda = Expression.Lambda( funcType, body, constructorParameterExpression )
.Compile();
return lambda;
}
I think what could be the answer to your problem is Expression.Parameter.
I found a blog entry on geeks with blogs which utilizes this. The only downside of this solution is that you'd have to create new overloads for when you need more than three arguments. But I guess this could be easily solved with params object[].

Expression.Lambda variable '' of type 'System.String' referenced from scope '', but it is not defined

I am trying to build a system that loads function delegates to a dictionary and then they can be called from anywhere in the environment asking the dictionary for the delegate.
My functions are of a format Func<string, string>.
My code is
var methods = typeof(Keywords)
.GetMethods()
.Where(mt => mt.GetCustomAttributes(typeof(KDTAttribute), false).Count() > 0);
foreach (var method in methods)
{
string key = ((KDTAttribute)method.GetCustomAttributes(typeof(KDTAttribute), false)[0]).Keyword;
var combinedArgumentsExp = new Expression[] { Expression.Parameter(typeof(string),"param") };
var mtCall = Expression.Call(Expression.Constant(me), method,combinedArgumentsExp);
ParameterExpression targetExpr = Expression.Parameter(typeof(string), "param");
Func<string, string> result = Expression.Lambda<Func<string, string>>(mtCall, targetExpr).Compile();
retVal.Add(key, result);
}
I get the exception on the Expression.Lambda line :
variable 'param' of type 'System.String' referenced from scope '', but it is not defined.
P.S:
If there is a better way to load delegates to a dictionary at run time I'll be happy for any suggestions.
You're calling Expression.Parameter twice, which gives you different expressions. Don't do that - just call it once, and use that ParameterExpression both places you need it:
var parameter = Expression.Parameter(typeof(string),"param");
string key = ((KDTAttribute)method.GetCustomAttributes(typeof(KDTAttribute), false)[0]).Keyword;
var mtCall = Expression.Call(Expression.Constant(me), method, parameter);
var result = Expression.Lambda<Func<string, string>>(mtCall, parameter).Compile();

How Do I Create an Expression<Func<>> with Type Parameters from a Type Variable

I'd like to write a statement like the following:
Expression<Func<AClass, bool>> filter = x => true;
Except instead of AClass, I'd like to use a Type variable determined at runtime. So, something conceptually like this:
Type aClassType = methodParameter.GetType();
Expression<Func<aClassType, bool>> filter = x => true;
Obviously the syntax will be quite a bit different. I'm assuming I'll need to use some sort of reflection or other fanciness.
End Goal
The end goal here is a bit complex, so I've simplified things immensely for the above example. The actual .Where call that is going to use this delegate looks more like this:
var copy = iQ;
...
copy = copy.Where( p1 => iQ.Where( p2 => pgr2.Key == p1.Key && p2.DataField == column.DataField && p2.ColumnText.Contains( requestValue ) ).Any() );
All of the properties of p1 and p2 are properties of a parent class of the element type of the IQueryable iQ. The Type variable I'd like to create will be the actual element type of iQ, i.e. the child class.
How do I do it?
Current Progress
Based on the answers below, I've written this test code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace IQueryableWhereTypeChange {
class Program {
static void Main( string[] args ) {
var ints = new List<ChildQueryElement>();
for( int i = 0; i < 10; i++ ) {
ints.Add( new ChildQueryElement() { Num = i, Value = i.ToString() } );
}
IQueryable<ChildQueryElement> theIQ = ints.AsQueryable();
Type type = typeof(ChildQueryElement);
var body = Expression.Constant(true);
var parameter = Expression.Parameter(type);
var delegateType = typeof(Func<,>).MakeGenericType(type, typeof(Boolean));
var lambda = Expression.Lambda( delegateType, body, parameter );
Console.WriteLine( lambda.GetType() );
dynamic copy = theIQ;
Type copyType1 = copy.GetType().GetGenericArguments()[ 0 ];
Type elementType1 = ((IQueryable)copy).ElementType;
Console.WriteLine( "copyType1 : " + copyType1.ToString() );
Console.WriteLine( "elementType1 : " + elementType1.ToString() );
copy = Queryable.Where( copy, lambda );
Type copyType2 = copy.GetType().GetGenericArguments()[ 0 ];
Type elementType2 = ((IQueryable)copy).ElementType;
Console.WriteLine( "copyType2 : " + copyType2.ToString() );
Console.WriteLine( "elementType2 : " + elementType2.ToString() );
}
}
public class ParentQueryElement {
public int Num { get; set; }
}
public class ChildQueryElement : ParentQueryElement {
public string Value { get; set; }
}
}
That program has this output:
System.Linq.Expressions.Expression`1[System.Func`2[IQueryableWhereTypeChange.ChildQueryElement,System.Boolean]]
copyType1 : IQueryableWhereTypeChange.ChildQueryElement
elementType1 : IQueryableWhereTypeChange.ChildQueryElement
Unhandled Exception:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:
The best overloaded method match for
'System.Linq.Queryable.Where<IQueryableWhereTypeChange.ChildQueryElement>(
System.Linq.IQueryable<IQueryableWhereTypeChange.ChildQueryElement>,
System.Linq.Expressions.Expression<System.Func<IQueryableWhereTypeChange.ChildQueryElement,bool>>
)'
has some invalid arguments
at CallSite.Target(Closure , CallSite , Type , Object , LambdaExpression )
at System.Dynamic.UpdateDelegates.UpdateAndExecute3[T0,T1,T2,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2)
at IQueryableWhereTypeChange.Program.Main(String[] args)
I'd like to get this working before I try and replicate my complicated predicate.
I find the exception rather baffling, as the output for lambda.GetType() matches the type in the exception almost exactly. The only difference is System.Boolean versus bool, but that shouldn't matter.
You need to create the appropriate delegate type, then pass that to the Expression.Lambda method. For example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
class Test
{
static void Main()
{
var type = typeof(string);
var body = Expression.Constant(true);
var parameter = Expression.Parameter(type);
var delegateType = typeof(Func<,>).MakeGenericType(type, typeof(bool));
dynamic lambda = Expression.Lambda(delegateType, body, parameter);
Console.WriteLine(lambda.GetType()); // Expression<string, bool>
}
}
Now of course your body would normally not just be a constant - but we don't know what you need to do there. From your edited question, it looks like you do have some statically typed knowledge of the type, otherwise you wouldn't be able to express that lambda expression. So either you need to build up the expression tree manually to be the equivalent of the lambda expression (using Expression.Property etc) or create one expression tree from what you know, and then use an expression tree visitor to adapt that to the actual type.
EDIT: Note that the type of lambda must be dynamic for it to work as the second argument to Queryable.Where, otherwise the execution-time version of the C# compiler will use the static type of the variable (which would just be LambdaExpression) to constrain overload resolution. You want it to match the actual Expression<TDelegate> type, which you can't express at compile-time, so you need to use dynamic.
You'll need to create an expression with a body set to the constant true and a parameter of the type of your type. There is an Expression to do each of these things:
Type type = typeof(object);
var lambda = Expression.Lambda(
Expression.Constant(true),
Expression.Parameter(type, "parameter"));
You cant pass the Type as ageneric parameter, this is not the idea of a generic, what you want is simply a parameter to a method for instance.

Expressions call constructor with a parameter and set its value

i am trying to call an parameterized constructor from an expression instead of using the default ctor. this is the code that gets the constructor parameter(s):
ConstructorInfo ci = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, CallingConventions.HasThis, new[] { typeof(bool) }, new ParameterModifier[] { });
ParameterInfo[] paramsInfo = ci.GetParameters();
//create a single param of type object[]
ParameterExpression param = Expression.Parameter(typeof(bool), "il");
Expression[] argsExp = new Expression[paramsInfo.Length];
//pick each arg from the params array
//and create a typed expression of them
for (int i = 0; i < paramsInfo.Length; i++)
{
Expression index = Expression.Constant(i);
Type paramType = paramsInfo[i].ParameterType;
Expression paramAccessorExp = param;
//Expression.ArrayIndex(param, index);
Expression paramCastExp =
Expression.Convert(paramAccessorExp, paramType);
argsExp[i] = param;
}
NewExpression ci2 = Expression.New(ci, argsExp);
But if i try to compile the lambda expression i am getting the following error:
variable 'il' of type 'System.Boolean' referenced from scope '', but it is not defined"
What am i missing? Any help and/ or hint is appreciated.
You define a parameter called li in the 4th line of your code. In order to use this in a lambda expression, you need to have a scope in which this parameter is defined. You have two choices:
Create a BlockExpression that contains param as a local variable. Then use this expression as the body of your lambda expression.
Use param as a parameter in your LambdaExpression.
If you use option 1, you'll also have to initialize the variable. Otherwise you'll get a different kind of error message.
EDIT
There are two problems with the additional code you posted:
You need to use the same parameter object throughout your expression tree. Having the same name and type does not make two Parameter objects equal. I would simply move everything up to and including creating the lambda to the ConvertThis method so you can reuse the param variable. You can then just compile the return value of ConvertThis to get your delegate.
When creating the BlockExpression, you need to pass param in as a local variable. You do this by adding an argument, new ParameterExpression[] { param } to the method.

Categories