Linq Expression referenced from scope but its not defined - c#

I am trying to create a Action that uses two parameters, one of the parameters are the class instance and the other is a object array. The object array types are unknown as I find the methods with a attribute, so I gave the below a try but the expr.Compile() throws an error
variable 'arg1[0]' of type 'System.Object' referenced from scope '', but it is not defined
public static T BuildDelegate<T>(MethodInfo method)
{
var dgtMi = typeof(T).GetMethod("Invoke");
var dgtParams = dgtMi.GetParameters();
var preDeterminedParams = new ParameterExpression[2]
{
Expression.Parameter(dgtParams[0].ParameterType, "arg0"),
Expression.Parameter(typeof(object[]), "arg1")
};
var methodParams = method.GetParameters();
var paramThis = Expression.Convert(preDeterminedParams[0], method.DeclaringType);
var paramsToPass = CreateParam(methodParams);
var expr = Expression.Lambda<T>(
Expression.Call(paramThis, method, paramsToPass),
preDeterminedParams);
return expr.Compile();
}
private static Expression[] CreateParam(ParameterInfo[] parameters)
{
var expressions = new Expression[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
{
//Trying to create a placeholder for any objects that might be passed through arg1
expressions[i] = Expression.Convert(
Expression.Parameter(typeof(object), "arg1[" + i + "]"),
parameters[i].ParameterType);
}
return expressions;
}
expr looks like this before trying to compile
(arg0, arg1) => Convert(arg0, Test).TestingInvocation(Convert(arg1[0], String), Convert(arg1[1], Int32), Convert(arg1[2], Single)
I am guessing since arg1[0] was created in CreateParam() as a place holder it does not have a reference value. Not sure how to create a holder, so it can have some sort of reference.
Essentially I am trying to accomplish this after the expression is compiled
Action<T(anyclass reference), object[] (unknown params)>
(arg0, arg1)=>{ Convert(arg0, T).Method(Convert(arg1[0], ToUnknownType))}

In the following snippet,
//...
//Trying to create a placeholder for any objects that might be passed through arg1
expressions[i] = Expression.Convert(
Expression.Parameter(typeof(object), "arg1[" + i + "]"),
parameters[i].ParameterType);
//...
You are basically doing arg1[0] => ... which is not a valid expression.
You are most likely looking for Expression.Array* related calls in order to access the array index.
public static T BuildDelegate<T>(MethodInfo method) {
var dgtMi = typeof(T).GetMethod("Invoke");
var dgtParams = dgtMi.GetParameters();
var preDeterminedParams = new ParameterExpression[2] {
Expression.Parameter(dgtParams[0].ParameterType, "arg0"),
Expression.Parameter(typeof(object[]), "arg1")
};
ParameterInfo[] methodParams = method.GetParameters();
var paramThis = Expression.Convert(preDeterminedParams[0], method.DeclaringType);
// arg1 =>
var arg1 = preDeterminedParams[1];
// arg1 => Convert(arg1[0], SomeType), Convert(arg1[1], SomeType), ....
var paramsToPass = CreateParam(arg1, methodParams);
var expr = Expression.Lambda<T>(
Expression.Call(paramThis, method, paramsToPass),
preDeterminedParams);
return expr.Compile();
}
private static Expression[] CreateParam(ParameterExpression arg1, ParameterInfo[] parameters) {
var expressions = new Expression[parameters.Length];
for (int i = 0; i < parameters.Length; i++) {
//arg1 => Convert(arg1[i], SomeType)
expressions[i] = Expression.Convert(
Expression.ArrayIndex(arg1, Expression.Constant(i)), parameters[i].ParameterType
);
}
return expressions;
}

Related

C# - Creating lambda functions using expression trees of an arbitrary delegate type

I am trying to create a runtime lambda function of an arbitrary type, that collects the arguments, that were supplied to it, in a list of objects and passes them to another method of type void Method(List<object> list) to process them. I wrote this code, but got really confused with the result:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace LambdaTest
{
class LambdaCreator
{
ParameterExpression[] Parameters;
int Index = 0;
public ParameterExpression Next()
{
return Parameters[Index++];
}
public void ResetIndex()
{
Index = 0;
}
public void Bar(List<object> parameters)
{
foreach (var p in parameters)
{
PrintType(p);
}
}
public void PrintType(object arg)
{
Console.WriteLine(arg.GetType().Name);
}
public T CreateLambda<T>() where T : class
{
var barMethod = GetType().GetMethod("Bar");
Parameters = typeof(T).GetMethod("Invoke")
.GetParameters()
.Select(x => Expression.Parameter(x.ParameterType))
.ToArray();
var parametersCount = Expression.Constant(Parameters.Length);
var listType = typeof(List<object>);
var list = Expression.Variable(listType);
var index = Expression.Variable(typeof(int));
var thisObject = Expression.Constant(this);
var resetIndex = GetType().GetMethod("ResetIndex");
var next = GetType().GetMethod("Next");
var printType = GetType().GetMethod("PrintType");
var add = listType.GetMethod("Add");
var breakLabel = Expression.Label();
var block = Expression.Block(
new ParameterExpression[] { list, index },
Expression.Call(thisObject, printType, Parameters.FirstOrDefault()),
Expression.Call(thisObject, resetIndex),
Expression.Assign(list, Expression.New(listType)),
Expression.Loop(
Expression.Block(
Expression.IfThen(Expression.GreaterThanOrEqual(index, parametersCount), Expression.Break(breakLabel)),
Expression.Call(list, add, Expression.Call(thisObject, next)),
Expression.AddAssign(index, Expression.Constant(1))
),
breakLabel
),
Expression.Call(thisObject, barMethod, list)
);
var lambda = Expression.Lambda(typeof(T), block, Parameters);
var compiledLambda = lambda.Compile() as T;
return compiledLambda;
}
}
class Program
{
delegate void Foo(string a, int b);
static void Main(string[] args)
{
var test = new LambdaCreator();
var l = test.CreateLambda<Foo>();
l("one", 2);
}
}
}
The output of the program was:
String
PrimitiveParameterExpression`1
PrimitiveParameterExpression`1
I was expecting to get:
String
String
Int32
Somehow I lose the values of the arguments when I put them in a list and pass it to the Bar method.
Can someone tell me where is the problem I how can I fix it. Or is there another way to collect the arguments and pass them through? I'm really new to this expression trees stuff. Thanks in advance!
You can create a NewArrayExpression using the Parameters array while constructing the lambda function, prior to the Expression.Loop block, and modify the calling code to access the array, like so:
// Declare a paramArray parameter to use inside the Expression.Block
var paramArray = Expression.Parameter(typeof(object[]), "paramArray");
var block = Expression.Block(
new ParameterExpression[] { list, index, paramArray }, // pass in paramArray here
Expression.Call(thisObject, printType, Parameters.FirstOrDefault()),
Expression.Call(thisObject, resetIndex),
Expression.Assign(list, Expression.New(listType)),
/* Assign the array - make sure to box value types using Expression.Convert */
Expression.Assign(
paramArray,
Expression.NewArrayInit(
typeof(object),
Parameters.Select(p => Expression.Convert(p, typeof(object))))),
Expression.Loop(
Expression.Block(
Expression.IfThen(Expression.GreaterThanOrEqual(index, parametersCount), Expression.Break(breakLabel)),
//Expression.Call(list, add, Expression.Call(thisObject, next)),
Expression.Call(list, add, Expression.ArrayIndex(paramArray, index)), // use the paramArray here
Expression.AddAssign(index, Expression.Constant(1))
),
breakLabel
),
Expression.Call(thisObject, barMethod, list)
);
The rest is unchanged - this code replaces the var block = ... statement entirely. Works as you specified.
This happens because this call:
Expression.Call(thisObject, printType, Parameters.FirstOrDefault())
Is actually compiled to something like:
this.PrintType(a)
Where a is your delegate parameter, while this:
Expression.Call(list, add, Expression.Call(thisObject, next))
Is compiled to something like:
this.PrintType(this.Next())
One of the options would be to modify print method:
public void PrintType(object arg)
{
if(arg is ParameterExpression expr)
{
Console.WriteLine(expr.Type.Name);
}
else
{
Console.WriteLine(arg.GetType().Name);
}
}
To fill list you can just create an corresponding expression:
var list = Expression.Variable(listType);
var exprs = new List<Expression>
{
Expression.Call(thisObject, resetIndex),
Expression.Assign(list, Expression.New(listType)),
};
for (int i = 0; i < #params.Length; i++)
{
var ex = Expression.Call(list, add, Expression.Convert(#params[i], typeof(object)));
exprs.Add(ex);
}
exprs.Add(Expression.Call(thisObject, barMethod, list));
var block = Expression.Block(new[] {list}, exprs);
Or use var property = Expression.PropertyOrField(thisObject, nameof(Parameters)); (with changing Parameters to List<object>, assigning new list to it and deleting block parameters) instead of list.

How to handle casting delegate of anonymous type <T>, to delegate of T for use in Where<T>() method of an IEnumerable<T>

Let me preface this by saying, I am noob, and I know not what I do. So, if there is a better way of doing this, I am all ears.
Currently, I am working on project for which I need to be able to coerce a data source into a List<T>, where T is an anonymous type, and filter it using lambda expressions, or create lambda expressions on the fly, save them to a database. I have already created a static wrapper class for System.Linq.Dynamic.Core, called RunTimeType, that has methods which allow me to create an anonymous type from some data source, and then create a List<> of that anonymous type. After both of the anontype and List<anontype> are created, I am using an existing fluent interface to create an Expression<Func<T, bool>>. Once I build the Expression and compile it,
I either want to execute it, or I want to convert it to a string and save it to a database, xml file, etc., for later use.
Case 1:
When compiling and then immediately executing the expression, I am good up until this line:
var testList = anonList.Where(castedExp).ToList();
where i recieve the following error:
Error CS1973 C# has no applicable method named 'Where' but appears to have an extension method by that name.
Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax.
This makes sense, because filter is declared as a dynamic, which I am forced to do, otherwise compiler would complain with the following:
Error CS1061 'object' does not contain a definition for 'By' and no accessible extension method 'By'
accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)
Case2:
As for the case of building the expression, converting it to a string, and then compiling to a valid Func<T,TResult>, I am good up until this line:
var castedExp = (Func<dynamic, bool>)compileExp;
where i recieve the following error:
Error System.InvalidCastException 'System.Func2[<>f__AnonymousType02[System.String,System.String],System.Boolean]' to type 'System.Func`2[System.Object,System.Boolean]'.'
However, I know that if I don't explicity cast to Func<dynamic, bool>, the compiler will complain with the following:
Error CS1503 Argument 2: cannot convert from 'System.Delegate' to 'System.Func<dynamic, bool>'.
So, my question is, how do I get around both of these situations, while still maintaining the ability use the anonymous type. Just to clarify again, I am forced to create an anonymous type because, I will not know what data set that I will be getting at run time, as these data sets are completely dynamic.
I want to reiterate, that I am open to doing this in a different way as long as the project's constraints are met. Frankly, I have been working on this for a while, I am out of ideas, and I need some guidance.
Below is all of the relevant code.
Test code:
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using ExpressionBuilder.Generics;
using ExpressionBuilder.Common;
using System.Linq;
using System.Linq.Dynamic;
using System.Linq.Dynamic.Core;
using ExpressionBuilterTest.TestImplementations;
namespace ExpressionBuilterTest
{
class Program
{
static void Main(string[] args)
{
//test Data source
object[,] arrayTest = new object[3, 2];
arrayTest[0, 0] = "Field1";
arrayTest[1, 0] = "X1";
arrayTest[2, 0] = "Y1";
arrayTest[0, 1] = "Field2";
arrayTest[1, 1] = "X2";
arrayTest[2, 1] = "Y2";
var anonType = RunTimeType.Create(arrayTest);
var anonList = RunTimeType.CreateGenericList(anonType, arrayTest);
//Creation of List<anonymous> type
var anonList = CreateGenericList(anonType, arrayTest);
//Creation of List<anonymous> type
Type genericFilter = typeof(Filter<>);
Type constructedClass = genericFilter.MakeGenericType(anonType);
//*************************Case 1*************************
/*
use dynamic otherwise compiler complains about accessing
methods on the instance of the filter object
*/
dynamic filter = Activator.CreateInstance(constructedClass);
filter.By("Field1", Operation.Contains, " X1 ")
.Or.By("Field2", Operation.Contains, " X2 ");
//returns Expression<Func<T, bool>>
var lamda = filter.GetExpression();
//Error CS1973
IEnumerable<dynamic> testList = anonList.Where(castedExp).ToList();
Console.WriteLine(testList.Count().ToString());
Console.WriteLine("\n");
//*************************Case 2*************************
//convert to string
string expString = lamda.Body.ToString().Replace("AndAlso", "&&").Replace("OrElse", "||");
// simulation of compiling an expression from a string which would be returned from a database
var param = Expression.Parameter(anonType, ExpressionParameterName.Parent);
var exp = System.Linq.Dynamic.DynamicExpression.ParseLambda(new ParameterExpression[] { param }, typeof(bool), expString);
var compiledExp = exp.Compile();
//*******************************************************
//Error CS1973
'System.Func`2[<>f__AnonymousType0`2[System.String,System.String],System.Boolean]' to type 'System.Func`2[System.Object,System.Boolean]'.'
var castedExp = (Func<dynamic, bool>)compileExp;
//*******************************************************
var testList2 = anonList.Where(castedExp).ToList();
Console.WriteLine(testList2.Count().ToString());
Console.ReadKey();
}
}
}
RunTimeType Class:
(for brevity, I have omitted the the overloads for the Create and CreateGenericList methods)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Runtime.CompilerServices;
namespace ExpressionBuilterTest.TestImplementations
{
public static class RunTimeType
{
/// <summary>
/// Creates an anonymous type from a 2d array that includes headers
/// </summary>
public static Type Create<T>(T[,] fieldNameAndValues)
{
IList<System.Linq.Dynamic.Core.DynamicProperty> properties = new List<System.Linq.Dynamic.Core.DynamicProperty>();
int columnCount = fieldNameAndValues.GetLength(1);
for (int jj = 0; jj < columnCount; jj++)
properties.Add(new System.Linq.Dynamic.Core.DynamicProperty(fieldNameAndValues[0, jj].ToString(), fieldNameAndValues[1, jj].GetType()));
return DynamicClassFactory.CreateType(properties);
}
/// <summary>
/// Creates an IEnumerable<dynamic>, where dynamic is an anonymous type, from a 2d array
/// </summary>
/// <param name="type">Anonymous type</param>
/// <param name="data">2 dimensional array of data</param>
public static IEnumerable<dynamic> CreateGenericList<T>(Type anonType, T[,] data)
{
ThrowIfNotAnonymousType(anonType);
dynamic dynoObject = Activator.CreateInstance(anonType);
var fieldNames = dynoObject.GetDynamicMemberNames();
Type genericListType = typeof(List<>);
Type constructedClass = genericListType.MakeGenericType(anonType);
dynamic list = (IEnumerable<dynamic>)Activator.CreateInstance(constructedClass);
int rowCount = data.GetLength(0);
int jj;
for (int ii = 1; ii < rowCount; ii++) //skip first row
{
jj = 0;
foreach (var field in fieldNames)
anonType.GetProperty(field).SetValue(dynoObject, data[ii, jj], null);
jj++;
list.Add(dynoObject);
}
return list;
}
private static void ThrowIfNotAnonymousType(Type type)
{
if (!IsAnonymousType(type))
throw new Exception("'anonType' must be an anonymous type");
}
//https://stackoverflow.com/questions/1650681/determining-whether-a-type-is-an-anonymous-type
private static Boolean IsAnonymousType(Type type)
{
Boolean hasCompilerGeneratedAttribute = type.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Count() > 0;
Boolean nameContainsAnonymousType = type.FullName.Contains("AnonymousType");
Boolean isAnonymousType = hasCompilerGeneratedAttribute && nameContainsAnonymousType;
return isAnonymousType;
}
}
}
Update:
I incorporated #CSharpie's answer, and tailored it to fit my implementation. Everything compiles, however, I am not getting the correct output (see comments in code body).
static void Main(string[] args)
{
object[,] arrayTest = new object[3, 2];
arrayTest[0, 0] = "Field1";
arrayTest[1, 0] = "X1";
arrayTest[2, 0] = "Y1";
arrayTest[0, 1] = "Field2";
arrayTest[1, 1] = "X2";
arrayTest[2, 1] = "Y2";
var anonType = RunTimeType.Create(arrayTest);
var anonList = RunTimeType.CreateGenericList(anonType, arrayTest);
Type targetType = anonType;
Type genericFilter = typeof(Filter<>);
Type constructedClass = genericFilter.MakeGenericType(targetType);
dynamic filter = Activator.CreateInstance(constructedClass);
//Dynamically build expression
filter.By("Field1", Operation.Contains, "X")
.Or.By("Field2", Operation.Contains, "2");
//Returns Expression<Func<anonType, bool>>
var lamda = filter.GetExpression();
string expString = lamda.Body.ToString();
expString = expString.Replace("AndAlso", "&&").Replace("OrElse", "||");
/*
Prints: (((x.Field1 != null) && x.Field1.Trim().ToLower().Contains("X".Trim().ToLower())) || ((x.Field2 != null) &&
x.Field2.Trim().ToLower().Contains("2".Trim().ToLower())))
*/
Console.WriteLine(expString);
ParameterExpression param = Expression.Parameter(targetType, ExpressionParameterName.Parent);
LambdaExpression exp = System.Linq.Dynamic.DynamicExpression.ParseLambda(new ParameterExpression[] { param }, typeof(bool), expString);
Delegate compileExp = exp.Compile();
MethodInfo whereMethod = typeof(Enumerable).GetMethods().Single(m =>
{
if (m.Name != "Where" || !m.IsStatic)
return false;
ParameterInfo[] parameters = m.GetParameters();
return parameters.Length == 2 && parameters[1].ParameterType.GetGenericArguments().Length == 2;
});
MethodInfo finalMethod = whereMethod.MakeGenericMethod(anonType);
IEnumerable resultList = (IEnumerable)finalMethod.Invoke(null, new object[] { anonList, compileExp });
/*
Prints Nothing but should print the following:
X1 X2
*/
foreach (dynamic val in resultList)
{
Console.WriteLine(val.Field1 + "/t" + val.Field2);
}
Console.ReadKey();
}
Final Update:
For those that are interested, I finally got this working. I figured out that my CreateGenericList method, was returning a list of only the first instance of my anonymous type. Saying that CreateGenericList should be become:
Reference: https://github.com/StefH/System.Linq.Dynamic.Core/blob/master/src/System.Linq.Dynamic.Core/DynamicClass.cs
/// <summary>
/// Creates an IEnumerable<dynamic>, where dynamic is an anonymous type, from a 2d array
/// </summary>
/// <param name="type">Anonymous type</param>
/// <param name="data">2 dimensional array of data</param>
public static IEnumerable<dynamic> CreateGenericList<T>(Type anonType, T[,] data)
{
ThrowIfNotAnonymousType(anonType);
Type genericListType = typeof(List<>);
Type constructedClass = genericListType.MakeGenericType(anonType);
dynamic list = (IEnumerable<dynamic>)Activator.CreateInstance(constructedClass);
//first instance
dynamic dynoObject = Activator.CreateInstance(anonType);
//System.Linq.Dynamic.Core.DynamicClass.GetDynamicMemberNames()
var fieldNames = dynoObject.GetDynamicMemberNames();
int rowCount = data.GetLength(0);
int jj;
for (int ii = 1; ii < rowCount; ii++) //skip first row
{
jj = 0;
foreach (var field in fieldNames)
{
//System.Linq.Dynamic.Core.DynamicClass.SetDynamicPropertyValue()
dynoObject.SetDynamicPropertyValue(field,data[ii, jj]);
jj++;
}
list.Add(dynoObject);
//create a new instance for each iteration of the loop
dynoObject = Activator.CreateInstance(anonType);
}
return list;
}
And then Main becomes:
static void Main(string[] args)
{
object[,] arrayTest = new object[3, 2];
arrayTest[0, 0] = "Field1";
arrayTest[1, 0] = "X1";
arrayTest[2, 0] = "blah";
arrayTest[0, 1] = "Field2";
arrayTest[1, 1] = "Y1";
arrayTest[2, 1] = "Y2";
var anonType = RunTimeType.Create(arrayTest);
var anonList = RunTimeType.CreateGenericList(anonType, arrayTest);
Type genericFilter = typeof(Filter<>);
Type constructedClass = genericFilter.MakeGenericType(anonType);
dynamic filter = Activator.CreateInstance(constructedClass);
//Dynamically build expression
filter.By("Field1", Operation.Contains, "blah")
.Or.By("Field2", Operation.Contains, "2");
//Returns Expression<Func<anonType, bool>>
var lamda = filter.GetExpression();
//Prints: System.Func`2[<>f__AnonymousType0`2[System.String,System.String],System.Boolean]
Console.WriteLine(lamda.Compile().ToString());
Console.WriteLine("\n");
string expBodyString = lamda.Body.ToString();
/*
Prints: (((x.Field1 != null) AndAlso x.Field1.Trim().ToLower().Contains("blah".Trim().ToLower()))
OrElse ((x.Field2 != null) AndAlso x.Field2.Trim().ToLower().Contains("2".Trim().ToLower())))
*/
Console.WriteLine(expBodyString);
Console.WriteLine("\n");
expBodyString = expBodyString.Replace("AndAlso", "&&").Replace("OrElse", "||");
/*
Prints: (((x.Field1 != null) && x.Field1.Trim().ToLower().Contains("blah".Trim().ToLower())) || ((x.Field2 != null)
&& x.Field2.Trim().ToLower().Contains("2".Trim().ToLower())))
*/
Console.WriteLine(expBodyString);
Console.WriteLine("\n");
ParameterExpression param = Expression.Parameter(anonType, ExpressionParameterName.Parent);
LambdaExpression exp = System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(new ParameterExpression[] { param }, typeof(bool), expBodyString);
/*
Prints: (((x.Field1 != null) AndAlso x.Field1.Trim().ToLower().Contains("blah".Trim().ToLower()))
OrElse ((x.Field2 != null) AndAlso x.Field2.Trim().ToLower().Contains("2".Trim().ToLower())))
*/
Console.WriteLine(exp.Body.ToString());
Console.WriteLine("\n");
Delegate compileExp = exp.Compile();
//Prints: System.Func`2[<>f__AnonymousType0`2[System.String,System.String],System.Boolean]
Console.WriteLine(compileExp.ToString());
Console.WriteLine("\n");
MethodInfo whereMethod = typeof(Enumerable).GetMethods().Single(m =>
{
if (m.Name != "Where" || !m.IsStatic)
return false;
ParameterInfo[] parameters = m.GetParameters();
return parameters.Length == 2 && parameters[1].ParameterType.GetGenericArguments().Length == 2;
});
MethodInfo finalMethod = whereMethod.MakeGenericMethod(anonType);
IEnumerable resultList = (IEnumerable)finalMethod.Invoke(null, new object[] { anonList, compileExp });
//Prints: blah Y2
foreach (dynamic val in resultList)
{
Console.WriteLine(val.Field1 + "\t" + val.Field2);
}
Console.ReadKey();
}
Here is a simple example without any additional nuget packages that calls the Enumerable.Where method.
I don't know what packages you are using exactly so you have to adept this to your requirements.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
public class Program
{
static void Main(string[] args)
{
var test = new {Foo = "bar"};
var test2 = new {Foo = "derp"};
// get the annonymous type
Type anonType = test.GetType();
// create a list of that annonymous type
IList genericList = (IList) Activator.CreateInstance(typeof(List<>).MakeGenericType(anonType));
genericList.Add(test);
genericList.Add(test2);
// Find the correct Enumerable.Where method
MethodInfo whereMethod = typeof(Enumerable).GetMethods().Single(m =>
{
if (m.Name != "Where" || !m.IsStatic)
return false;
ParameterInfo[] parameters = m.GetParameters();
return parameters.Length == 2 && parameters[1].ParameterType.GetGenericArguments().Length == 2;
});
// construct the finalmethod using generic type
MethodInfo finalMethod = whereMethod.MakeGenericMethod(anonType);
// define the Type of the Filter Func<anontype,bool>
Type filterType = typeof(Func<,>).MakeGenericType(anonType, typeof(bool));
// Build a simple filter expression
// this is mostly to subsitute for the missing packages you are using to create that filter func
ParameterExpression parameter = Expression.Parameter(anonType, "item");
MemberExpression member = Expression.Property(parameter, "Foo");
BinaryExpression euqalExpression = Expression.Equal(member, Expression.Constant("derp"));
LambdaExpression filterExpression = Expression.Lambda(filterType, euqalExpression, parameter);
Delegate filter = filterExpression.Compile();
Console.WriteLine("This is the Filter: {0}", filterExpression);
// Finally invoke and see it in action
IEnumerable result = (IEnumerable) finalMethod.Invoke(null, new object[] {genericList, filter});
foreach (dynamic o in result)
{
Console.WriteLine(o.Foo);
}
Console.ReadKey();
}
}
The thing here is, you need to construct the generic method yourself, which means you have to provide the generic arguments. In your case its the anonType.
That is what this line is doing
MethodInfo finalMethod = whereMethod.MakeGenericMethod(anonType);
Afther that I use System.Linq.Expressions to create a simple Func<anontype, bool> which represents the filter.
Update
This works for me noiw, the flaw must be that other stuff you are doing. I leave that up to you to figure out.
object[,] arrayTest = new object[3, 2];
arrayTest[0, 0] = "Field1";
arrayTest[1, 0] = "X1";
arrayTest[2, 0] = "derp";
arrayTest[0, 1] = "Field2";
arrayTest[1, 1] = "X2";
arrayTest[2, 1] = "Y2";
var anonType = RunTimeType.Create(arrayTest);
var anonList = ( IList)RunTimeType.CreateGenericList(anonType, arrayTest);
// define the Type of the Filter Func<anontype,bool>
Type filterType = typeof(Func<,>).MakeGenericType(anonType, typeof(bool));
// Build a simple filter expression
ParameterExpression parameter = Expression.Parameter(anonType, "item");
var property = anonType.GetProperty("Field1");
MemberExpression member = Expression.Property(parameter, property);
BinaryExpression euqalExpression = Expression.Equal(member, Expression.Constant("derp"));
MethodInfo whereMethod = typeof(Enumerable).GetMethods().Single(m =>
{
if (m.Name != "Where" || !m.IsStatic)
return false;
ParameterInfo[] parameters = m.GetParameters();
return parameters.Length == 2 && parameters[1].ParameterType.GetGenericArguments().Length == 2;
});
MethodInfo finalMethod = whereMethod.MakeGenericMethod(anonType);
LambdaExpression filterExpression = Expression.Lambda(filterType, euqalExpression, parameter);
Delegate filter = filterExpression.Compile();
Console.WriteLine("This is the Filter: {0}", filterExpression);
IEnumerable result = (IEnumerable) finalMethod.Invoke(null, new object[] {anonList, filter});
foreach (dynamic o in result)
{
Console.WriteLine(o.Field1);
}
Console.ReadKey();
Here is a minimal working code for what you seemingly want to do:
IEnumerable<dynamic> anonList = new dynamic[] {new {Test = "1"}, new {Test = "2"}};
Func<dynamic, bool> filterExpression = (d) => d.Test == "2";
var result = anonList.Where(filterExpression).ToList();
I assume that this does not solve your problem yet, so maybe you can elaborate on my simple example which things you do not have control of or what subtleties of your problem I missed.
For example I am not sure if your var lamda = filter.GetExpression(); can return Func<dynamic,bool> or if it does already.

C# lambda delegate call methods with reference variables

The below code works only for calling methods without reference parameters.
public delegate void testD2(params object[] args);
public static testD2 SetTestD2(MethodInfo method)
{
ParameterExpression param = Expression.Parameter(typeof(object[]), "args");
ParameterInfo[] paramsInfo = method.GetParameters();
Expression[] argsExp = new Expression[paramsInfo.Length];
for (int i = 0; i < paramsInfo.Length; i++)
{
Expression index = Expression.Constant(i);
Type paramType = paramsInfo[i].ParameterType;
//??? ByRef removed from type, becuse Expression.Call with ByRef parametrs lead to compile error
if (paramType.IsByRef == true)
paramType = paramType.GetElementType();
//??? and for this reason is not change of parameters permanent
Expression paramAccessorExp = Expression.ArrayIndex(param, index);
Expression paramCastExp = Expression.Convert(paramAccessorExp, paramType);
argsExp[i] = paramCastExp;
}
var blockExp = Expression.Call(method, argsExp);
LambdaExpression result = Expression.Lambda(typeof(testD2), blockExp, param); //change in param?
return (testD2)result.Compile();
}
I am thinking to change the input parameters passed with params keyword, or create new params for lambda, but I don't know how.
public class testCls
{
public void Test()
{
MethodInfo mi = typeof(XXX).GetMethod("TestMethod");
var compiledObject2 = XXX.SetTestD2(mi);
int k = 5;
compiledObject2(k); //k is not passed as ByRef
}
}
public static void TestMethod(ref int a)
{
a = a * a;
}
ref or out parameters are incompatible with params, I think that's a fundamental language or runtime limitation.
To do what you want, you can compile into strongly-typed delegate of the correct type. Expression.GetDelegateType call can create you that correct type; unlike params object[] there can be output and ref parameters.
Strongly typed delegate is a way, but requires extra code for various methods. That is why I tried for a general function to create general delegates.
To the keyword params, I think if the function is called classically DelegateSomeFunc (a, b, c,), then nothing is passed by reference. But if the function is called by a array, then parameters are passed by reference.
int k = 5;
string b = "aa";
object[] objArr = { k, b };
compiledObject2(k, b); //parameters passed by value
compiledObject2(objArr); //array passed by reference (params don't need create new array?)
Delegate call for example this function
public static void TestMethod(ref int a, string text)
{
a = a * a;
}
Theoretically is possible create delegate with function, but there is another problem - call function with array of exspressions parametrs rather then with array of expressions.
Code above in original post have a row
var blockExp = Expression.Call(method, argsExp);
But argsExp probably cannot return parameters changed by a function. For this reason i write input parameters in local expression variables, which called function can change and finally put the changed values in input parameter array.
public static Class1.testD2 SetTestD2(System.Reflection.MethodInfo method)
{
ParameterExpression param = Expression.Parameter(typeof(object[]), "args"); // Expression.Parameter(typeof(object[]), "args");
BinaryExpression[] byExp=null;
Expression[] argsExp = GetArgExp(method.GetParameters(), param, ref byExp);
ParameterExpression xxx = Expression.Variable(typeof(int));
ParameterExpression yyy = Expression.Variable(typeof(string));
var blockExp =
Expression.Block( new[] { xxx, yyy } //variables
, Expression.Assign(xxx, argsExp[0])
, Expression.Assign(yyy, argsExp[1])
, Expression.Call(method, xxx, yyy)
, Expression.Assign(Expression.ArrayAccess(param, Expression.Constant(0)), Expression.Convert(xxx, typeof(object))) //change input param array
) ;
LambdaExpression result = Expression.Lambda(typeof(testD2), blockExp, param);
return (testD2)result.Compile();
}
The function is just an example for changing the input parameter. Now is possible return changed parameter.
MethodInfo mi = typeof(Class1).GetMethod("TestMethod");
var compiledObject2 = Class1.SetTestD2(mi);
int k = 5;
string b = "aa";
object[] objArr = { k, b };
compiledObject2(k, b); //no changes
compiledObject2(objArr); //objArr[0] changed
But I do not know if function what create delegate can be modified to create a general delegate.

Extending Func to accept array parameter in C#

I have been using Func to create
Expression.Lambda<Func<object, object>>(block, paramValues).Compile();
Where block is Expression.Block that creates the required execution plan. paramValues is an array of Expression.Parameter values:
var paramValues = epDef.Value.Parameters.Select(p => Expression.Parameter(typeof(object), p.Name))
.ToArray();
I am trying to modify this to accept an array, so that I can use Func when I have more than 16 input parameters:
Expression.Lambda<Func<object[], object>>(block, paramValues).Compile();
However this gives me the following error:
Incorrect number of parameters supplied for lambda declaration
Can anyone help me with using Func to accept more than 16 parameters in this case? either using an array as input or by creating a custom delegate
#MethodMan:
I tried to implement the solution in that question, but I am getting the same error. From what I understood, changing the Expression.Parameter from typeof(object) to typeof(object[]) should have worked in my case, but I get the same error
EDIT:
Here's the modified version of what I tried from the linked version:
class Program
{
static void Main(string[] argas)
{
Type[] types = new Type[] { typeof(object[]) };
var constructorInfo = typeof(Program).GetConstructor(types);
var parameters = types.Select((t, i) => Expression.Parameter(t, "p" + i)).ToArray();
var someType1Exp = Expression.New(constructorInfo, parameters);
var inner = Expression.Lambda(someType1Exp, parameters);
var args = Expression.Parameter(typeof(object[]), "args");
var body = Expression.Invoke(inner,
parameters.Select((p, i) => Expression.Convert(Expression.ArrayIndex(args, Expression.Constant(i)), p.Type)).ToArray());
var outer = Expression.Lambda<Func<object[], object>>(body, args);
var func = outer.Compile();
object[] values = { 1, 123.45F, "abc" };
object obj = func(values);
Console.WriteLine(obj);
Console.WriteLine("test");
Console.ReadLine();
}
//public Program() { }
public Program(object[] values) { Console.WriteLine(values.ToString()); }
}
I get the following error:
System.InvalidCastException: 'Unable to cast object of type 'System.Int32' to type 'System.Object[]'.'
You're problem is you changed your lambda to take a single array argument, object[] but you are still passing in multiple arguments (paramValues). You need to pass in a single value, the Array. You will also need to modify your expression body to expect an array.
Consider before you had the equivalent of
(a,b,c,d) => new Program(a, b, c, d)
Now you want
(object[] a) => new Program((type0)a[0], (type1)a[1], (type2)a[2], (type3)a[3])
So you need to modify your code to reference the elements of the parameter array instead of the parameters directly:
class Program
{
static void Main(string[] argas)
{
var constructorInfo = typeof(Program).GetConstructors()[0];
var types = constructorInfo.GetParameters().Select(p => p.ParameterType);
var parm = Expression.Parameter(typeof(object[]), "args");
var parameters = types.Select((t, i) => Expression.Convert(Expression.ArrayIndex(parm, Expression.Constant(i)), t)).ToArray();
var someType1Exp = Expression.New(constructorInfo, parameters);
var outer = Expression.Lambda<Func<object[], object>>(someType1Exp, parm);
var func = outer.Compile();
object[] values = { 1, 123.45F, "abc" };
object obj = func(values);
Console.WriteLine(obj);
Console.WriteLine("test");
Console.ReadLine();
}
//public Program() { }
public Program(int val1, float val2, string val3) {
Console.WriteLine(val1);
Console.WriteLine(val2);
Console.WriteLine(val3);
}
}
If you want to pass the object[] to the constructor, as in
(object[] args) => new Program(args)
you can just do that directly (sorry for formatting, copied from my LINQPad):
public class Program {
void Main() {
var constructorInfo = typeof(Program).GetConstructors()[0];
var parm = Expression.Parameter(typeof(object[]), "args");
var someType1Exp = Expression.New(constructorInfo, parm);
var outer = Expression.Lambda<Func<object[], object>>(someType1Exp, parm);
var func = outer.Compile();
object[] values = { 1, 123.45F, "abc" };
object obj = func(values);
Console.WriteLine(obj);
Console.WriteLine("test");
}
// Define other methods and classes here
public Program(object[] parms) {
Console.WriteLine(parms.ToString());
}
}

dynamically create delegate for ctor

I'm trying to create generic factory class. Since Activator.CreateInstance is pretty slow, I decided to use delegates. The goal was call constructor any public constructor, regardless of parameters count. So I was going like this:
public void Register<VType>(TKey key, params object[] args) where VType : TType
{
ConstructorInfo ci = typeof(VType).GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, CallingConventions.HasThis, args.Select(a => a.GetType()).ToArray(), new ParameterModifier[] { });
if (ci == null)
throw new InvalidOperationException(string.Format("Constructor for type '{0}' was not found.", typeof(VType)));
var pExp = Expression.Parameter(args.GetType());
var ctorParams = ci.GetParameters();
var expArr = new Expression[ctorParams.Length];
var p = new ParameterExpression[ctorParams.Length];
for (var i = 0; i < ctorParams.Length; i++)
{
var ctorType = ctorParams[i].ParameterType;
var pName = ctorParams[i].Name;
var argExp = Expression.ArrayIndex(pExp, Expression.Constant(i));
var argExpConverted = Expression.Convert(argExp, ctorType);
expArr[i] = argExpConverted;
p[i] = Expression.Parameter(args[i].GetType(), pName);
}
var foo = Expression.Lambda(Expression.New(ci, expArr), p);
Delegate constructorDelegate = foo.Compile();
FactoryMap.Add(key, constructorDelegate);
}
And then - call delegate in Create method. With no parameters all goes well, but when I'm adding some - I'm getting InvalidOperationException - "variable '' of type 'System.Object[]' referenced from scope '', but it is not defined", after foo.Compile() call.
Why? How can I resolve this issue?
Below is a class that exposes an extention method that gives you a delegate for creating an instance of type T by calling the constructor that binds to specified paramArguments types.
public static class ConstructorCallExcentions
{
private static Dictionary<ConstructorInfo, Func<Object[], Object>> _constructors = new Dictionary<ConstructorInfo,Func<object[],object>> ();
private static object syncObject = new object();
public static Func<Object[], Object> CreateConstructor<T>(this T #this, params Type[] paramArguments)
{
ConstructorInfo cInfo = typeof(T).GetConstructor(paramArguments);
if (cInfo == null)
throw new NotSupportedException("Could not detect constructor having the coresponding parameter types");
Func<Object[], Object> ctor;
if (false == _constructors.TryGetValue (cInfo, out ctor))
{
lock (_constructors)
{
if (false == _constructors.TryGetValue (cInfo, out ctor))
{
// compile the call
var parameterExpression = Expression.Parameter(typeof(object[]), "arguments");
List<Expression> argumentsExpressions = new List<Expression>();
for (var i = 0; i < paramArguments.Length; i++)
{
var indexedAcccess = Expression.ArrayIndex(parameterExpression, Expression.Constant(i));
// it is NOT a reference type!
if (paramArguments [i].IsClass == false && paramArguments [i].IsInterface == false)
{
// it might be the case when I receive null and must convert to a structure. In this case I must put default (ThatStructure).
var localVariable = Expression.Variable(paramArguments[i], "localVariable");
var block = Expression.Block (new [] {localVariable},
Expression.IfThenElse (Expression.Equal (indexedAcccess, Expression.Constant (null)),
Expression.Assign (localVariable, Expression.Default (paramArguments [i])),
Expression.Assign (localVariable, Expression.Convert(indexedAcccess, paramArguments[i]))
),
localVariable
);
argumentsExpressions.Add(block);
}
else
argumentsExpressions.Add(Expression.Convert(indexedAcccess, paramArguments[i])); // do a convert to that reference type. If null, the convert is FINE.
}
// check if parameters length maches the length of constructor parameters!
var lengthProperty = typeof (Object[]).GetProperty ("Length");
var len = Expression.Property (parameterExpression, lengthProperty);
var invalidParameterExpression = typeof(ArgumentException).GetConstructor(new Type[] { typeof(string) });
var checkLengthExpression = Expression.IfThen (Expression.NotEqual (len, Expression.Constant (paramArguments.Length)),
Expression.Throw(Expression.New(invalidParameterExpression, Expression.Constant ("The length does not match parameters number")))
);
var newExpr = Expression.New(cInfo, argumentsExpressions);
var finalBlock = Expression.Block(checkLengthExpression, Expression.Convert(newExpr, typeof(Object)));
_constructors[cInfo] = ctor = Expression.Lambda(finalBlock, new[] { parameterExpression }).Compile() as Func<Object[], Object>;
}
}
}
return ctor;
}
}
To use it, for example supose you have this class:
public class Test
{
public Test(string s, int h)
{
Console.Write("aaa");
}
}
Then write this code:
var ctor = default(Test).CreateConstructor(typeof(string), typeof(int));
var newlyObject = ctor(new object[] { "john", 22 });
From your example, I saw that your intentions are to use the Delegate to invoke later any constructor. Instead of using Delegate and the DynamicInvoke API, use my
Func <Object[], Object>.
Why? Here is a couple of advantages that I have in mind right now:
1) DynamicInvoke is much slower than calling a direct typed delegate.
2) DynamicInvoke will break any stack trace in case of an exception. What I mean is that whenever an exception is thrown in the constructor, you will receive a TargetInvocationException instead of the real exception that happened. You can inspect the InnerException of that TargetInvocationException but ... clear is more work to do. Calling directly the typed delegate Func will save you from this issue.
Happy coding!

Categories