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)
);
Related
I have the following expression tree:
var collection = Expression.Variable(typeof(string[]), "testVar");
Expression[] arrayElements = new[] { Expression.Constant("helloWorld") };
var arrayInitialization = Expression.NewArrayInit(typeof(string), arrayElements);
var body = Expression.Assign(collection, arrayInitialization);
return Expression.Lambda<Func<string[]>>(body).Compile();
It throws an error at the final expression, about "testVar" being out of scope, unless I change the return statement to the following:
return Expression.Lambda<Func<string[]>>(Expression.Block(
typeof(string[]), new[] { collection }, body)).Compile();
Could someone explain why that is so? And if there is an alternative to not using a block, but still having a variable defined in the expression tree?
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).
I am trying to construct function that accepts more that one parameter. Lambda function accepting one parameter works fine. Here is the code.
var value = 22.55;
var method = typeof(TextWriter).GetMethod("WriteLine", new Type[] { value.GetType() });
ParameterExpression inputParameter = Expression.Parameter(typeof(TextWriter));
var block = Expression.Block(
inputParameter,
Expression.Call(inputParameter, method, Expression.Constant(value)));
var function = Expression.Lambda<Action<TextWriter>>(block, inputParameter).Compile();
function(Console.Out);
However when I add one more parameter that I do not even use then function throws null reference exception. I must be missing something but have no idea what. Here is the code that does not work:
var value = 22.55;
var method = typeof(TextWriter).GetMethod("WriteLine", new Type[] { value.GetType() });
ParameterExpression inputParameter = Expression.Parameter(typeof(TextWriter));
// Additional parameter here
ParameterExpression inputParameter2 = Expression.Parameter(typeof(double));
var block = Expression.Block(
// Function block accepts two parameters
new List<ParameterExpression>() { inputParameter, inputParameter2 },
Expression.Call(inputParameter, method, Expression.Constant(value)));
var function = Expression.Lambda<Action<TextWriter, double>>(block, inputParameter, inputParameter2).Compile();
// ....aaand null exception here. Why?
function(Console.Out, value);
What am I doing wrong here?
Judging by your comment, you're misunderstanding what you're passing to Block. The variables argument of Block is to define local variables, rather than parameters.
var block = Expression.Block(
// Function block accepts two parameters
new List<ParameterExpression>() { inputParameter, inputParameter2 },
Expression.Call(inputParameter, method, Expression.Constant(value)));
Should be simply
var block = Expression.Block(Expression.Call(inputParameter, method, Expression.Constant(value)));
What you're currently doing is equivelant to:
void function(TextWriter tw, double d) {
TextWriter tw = default(TextWriter);
double d = default(double);
tw.WriteLine(22.55);
}
While you actually just want:
void function(TextWriter tw, double d) {
tw.WriteLine(22.55);
}
Also, you're not actually using your second parameter. The complete working code is:
var method = typeof(TextWriter).GetMethod("WriteLine", new Type[] { typeof(double) });
ParameterExpression inputParameter = Expression.Parameter(typeof(TextWriter));
ParameterExpression inputParameter2 = Expression.Parameter(typeof(double));
var block = Expression.Block(Expression.Call(inputParameter, method, inputParameter2));
var function = Expression.Lambda<Action<TextWriter, double>>(block, inputParameter, inputParameter2).Compile();
function(Console.Out, 35.5);
Your first code worked because you used a different overload of block. Instead of declaring a local variable for inputParameter, you passed it as an expression itself. So, the code looked like this:
void function(TextWriter tw, double d) {
tw; //Illegal for the C# compiler, but not illegal as an expression. This will do nothing.
tw.WriteLine(22.55);
}
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();
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.