In C# 3.0 you can use Expression to create a class with the following syntax:
var exp = Expression.New(typeof(MyClass));
var lambda = LambdaExpression.Lambda(exp);
object myObj = lambda.Compile().DynamicInvoke();
But how do you use Expression to create an Anonymous class?
//anonymousType = typeof(new{ Name="abc", Num=123});
Type anonymousType = Expression.NewAnonymousType??? <--How to do ?
var exp = Expression.New(anonymousType);
var lambda = LambdaExpression.Lambda(exp);
object myObj = lambda.Compile().DynamicInvoke();
You're close, but you have to be aware that anonymous types don't have default constructors. The following code prints { Name = def, Num = 456 }:
Type anonType = new { Name = "abc", Num = 123 }.GetType();
var exp = Expression.New(
anonType.GetConstructor(new[] { typeof(string), typeof(int) }),
Expression.Constant("def"),
Expression.Constant(456));
var lambda = LambdaExpression.Lambda(exp);
object myObj = lambda.Compile().DynamicInvoke();
Console.WriteLine(myObj);
If you don't have to create many instances of this type, Activator.CreateInstance will do just as well (it's faster for a few instances, but slower for many). This code prints { Name = ghi, Num = 789 }:
Type anonType = new { Name = "abc", Num = 123 }.GetType();
object myObj = Activator.CreateInstance(anonType, "ghi", 789);
Console.WriteLine(myObj);
You can avoid using DynamicInvoke which is painfully slow. You could make use of type inference in C# to get your anonymous type instantiated generically. Something like:
public static Func<object[], T> AnonymousInstantiator<T>(T example)
{
var ctor = typeof(T).GetConstructors().First();
var paramExpr = Expression.Parameter(typeof(object[]));
return Expression.Lambda<Func<object[], T>>
(
Expression.New
(
ctor,
ctor.GetParameters().Select
(
(x, i) => Expression.Convert
(
Expression.ArrayIndex(paramExpr, Expression.Constant(i)),
x.ParameterType
)
)
), paramExpr).Compile();
}
Now you can call,
var instantiator = AnonymousInstantiator(new { Name = default(string), Num = default(int) });
var a1 = instantiator(new object[] { "abc", 123 }); // strongly typed
var a2 = instantiator(new object[] { "xyz", 789 }); // strongly typed
// etc.
You could use the AnonymousInstantiator method to generate functions to instantiate any anonymous type with any number of properties, just that you have to pass an appropriate example first. The input parameters have to be passed as an object array. If you worry boxing performance there then you have to write a custom instantiator which accepts just string and int as input parameters, but the use of such an instantiator will be a bit more limited.
Since an anonymous type doesn't have a default empty constructor, you cannot use the Expression.New(Type) overload ... you have to provide the ConstructorInfo and parameters to the Expression.New method. To do that, you have to be able to get the Type ... so you need to make a "stub" instance of the anonymous type, and use that to get the Type, and the ConstructorInfo, and then pass the parameters to the Expression.New method.
Like this:
var exp = Expression.New(new { Name = "", Num = 0 }.GetType().GetConstructors()[0],
Expression.Constant("abc", typeof(string)),
Expression.Constant(123, typeof(int)));
var lambda = LambdaExpression.Lambda(exp);
object myObj = lambda.Compile().DynamicInvoke();
Related
I would like to instantiate a (non-generic) class in C# (ClassToBeInstantiated) that has a public non-parameterless constructor via LINQ Expression.New inside an Expression.Block.
see update below, based on answer from #sweeper
Description
According to the documentation, the only overloads for Expression.New that do accept arguments require a ConstructorInfo argument. As I do not have access to that info beforehand, but have to retrieve this inside the Expression.Block.
So I am able to use an Expression.Call on the type ClassToBeInstantiated that is passed into the block.
However, all Expression.New overloads only accept either ConstructorInfo as an argument or for an instantiation if I want to pass arguments to the constructor. Type is only available for using a parameterless constructor.
I cannot use a ParameterExpression holding a ConstructorInfo either.
Question
So my question: is there a way around this with Expression.New? I know I can use ConstructorInfo.Invoke via another Expression.Call. But this seems akward to me, as -at least in my opinion- the Expression.New API should exactly do this for me.
Am I missing something?
Thanks for your help, comments and reply.
The class to be instantiated
Here is some additional information to further illustrate the case:
public class ClassToBeInstantiated
{
public ClassToBeInstantiated(int count) { }
}
Helper class to retrieve ConstructorInfo
public static class GetConstructor
{
public static ConstructorInfo GetNonGenericConstructorInfo(Type type, params Type[] typeArguments) =>
type.GetConstructor(typeArguments);
}
[Test]
public void InstantiateTypeViaExpressionNew()
{
var typeExpression = Expression.Parameter(typeof(Type), "type");
var countExpression = Expression.Parameter(typeof(int), "count");
var ctorExpression = Expression.Variable(typeof(ConstructorInfo), "constructorInfo");
var block = Expression.Block
(
new[] { ctorExpression },
Expression.Assign(ctorExpression, Expression.Call(typeof(GetConstructor), nameof(GetConstructor.GetNonGenericConstructorInfo),
Type.EmptyTypes, typeExpression, Expression.Constant(new[] { countExpression.Type }))),
Expression.New(/* error - obviously does not compile: ctorInfo */, countExpression)
);
var lambda = Expression.Lambda<Func<Type, int, object>>(block, typeExpression, countExpression);
var func = lambda.Compile();
var o = func.Invoke(typeof(ClassToBeInstantiated), 4);
var instance = o as ClassToBeInstantiated;
Assert.NotNull(instance);
}
Update with Expression.Call instead of Expression.New
I updated the code example from my original question, based on the answer from #Sweeper to provide a full example (in case someone is interested):
Updated helper class
Here, I added the field constructorInfoInvokeMethodInfo for retrieval of the MethodInfo for the ConstructorInfo.Invoke() method (to be called from inside the Expression.Block:
public static class GetConstructor
{
public static ConstructorInfo GetNonGenericConstructorInfo(Type type, params Type[] typeArguments) =>
type.GetConstructor(typeArguments);
public static readonly MethodInfo constructorInfoInvokeMethodInfo =
typeof(ConstructorInfo).GetMethod(nameof(ConstructorInfo.Invoke), new[] { typeof(object[]) });
}
Updated test for Expression.Block
Here, I replaced the (non-working) Expression.New with an Expression.Call to instantiate the type via ConstructorInfo.Invoke():
[Test]
public void InstantiateTypeViaExpressionCall()
{
var typeExpression = Expression.Parameter(typeof(Type), "type");
var countExpression = Expression.Parameter(typeof(int), "count");
var ctorExpression = Expression.Variable(typeof(ConstructorInfo), "constructorInfo");
var block = Expression.Block
(
new[] { ctorExpression },
Expression.Assign
(
ctorExpression,
Expression.Call(typeof(Activator), nameof(GetNonGenericConstructorInfo), Type.EmptyTypes, typeExpression, Expression.Constant(new[] { countExpression.Type }))
),
Expression.Call(ctorExpression, constructorInfoInvokeMethodInfo, Expression.NewArrayInit(typeof(object), Expression.Convert(countExpression, typeof(object))))
);
var lambda = Expression.Lambda<Func<Type, int, object>>(block, typeExpression, countExpression);
var func = lambda.Compile();
var o = func.Invoke(typeof(ClassToBeInstantiated), 4);
var instance = o as ClassToBeInstantiated;
Assert.NotNull(instance);
}
So my question: is there a way around this with Expression.New? I know I can use ConstructorInfo.Invoke via another Expression.Call.
This is exactly what you should do.
Think about the expression tree that you want to create. Since the final usage that you want to achieve looks like:
func.Invoke(typeof(ClassToBeInstantiated), 4);
func's expression must look like:
(type, count) => GetConstructor.GetNonGenericConstructorInfo(type, typeof(int)).Invoke(new object[] {count})
This seems to be what you were trying to do too, just with an extra local variable.
It cannot be:
(type, count) => new type(count)
Because new type(count) is not a valid expression. type is just a parameter.
This has nothing to do with new expressions that Expression.New creates. There are no NewExpressions in your desired expression at all. It would be very weird if Expression.New suddenly starts inserting ConstructorInfo.Invoke calls in the expression tree, because that is not what it is supposed to create at all.
You could use Expression.New here to create expressions such as:
(type, count) => new ClassToBeInstantiated(count)
But I don't think that's what you want... You want the type parameter to determine what type to instantiate, and assume that a one-parameter int constructor to be available, right?
Using sharplab.io, you can write the lambda expression you want, and see what code the compiler actually generates for building the expression tree. After cleaning up the code that generates for
Expression<Func<Type, int, object>> func =
(type, count) => GetConstructor.GetNonGenericConstructorInfo(type, typeof(int)).Invoke(new object[] {count});
You get:
MethodInfo getConstructorMethod = typeof(GetConstructor).GetMethod(nameof(GetConstructor.GetNonGenericConstructorInfo));
MethodInfo invokeMethod = typeof(ConstructorInfo).GetMethod(nameof(ConstructorInfo.Invoke), new Type[] { typeof(object[]) });
ParameterExpression typeParam = Expression.Parameter(typeof(Type), "type");
ParameterExpression countParam = Expression.Parameter(typeof(int), "count");
// GetNonGenericConstructorInfo(type, typeof(int))
MethodCallExpression instance = Expression.Call(null, getConstructorMethod,
typeParam, Expression.NewArrayInit(typeof(Type), Expression.Constant(typeof(int), typeof(Type)))
);
// // .Invoke(new object[] { count })
MethodCallExpression body = Expression.Call(instance, invokeMethod,
Expression.NewArrayInit(typeof(object), Expression.Convert(countParam, typeof(object)))
);
Expression<Func<Type, int, object>> func = Expression.Lambda<Func<Type, int, object>>(body, typeParam, countParam);
And func.Compile()(typeof(ClassToBeInstantiated), 4) as ClassToBeInstantiated is not null.
Say I have some function like so
public string TestValue(string hello, Guid world)
{
return hello + world;
}
Assuming that objectParams is a dictionary of object Dictionary<string, object> that map the parameter name to the value, I'm currently matching the parameter values to the method name like so:
var method = this.GetType().GetMethod("TestValue");
var methodParameters = method.GetParameters();
var paramMatcher = (from paramValue in objectParams
from methodParam in methodParameters
where param.Key == clrParam.Name
select (name: clrParam.Name,
type: clrParam.ParameterType,
value: paramValue.Value));
I then build the expression to call the method TestValue like so, but this is the part I'm having trouble with.
var paramExpress = (from param in paramMatcher
select Expression.Assign(Expression.Parameter(param.type, param.name), Expression.Constant(param.value)));
Func<object> result = Expression.Lambda<Func<object>(Expression.Call(Expression.Constant(this),
method, paramExpress)).Compile();
var res = result.Invoke(); //should return 'somestringxxxxxxx-xxxx...etc'
The issue is that I can't guarantee that the parameter values are in call order so I want to rely on the name of the parameters to call. I'm not sure on how to assign the constant values to their parameter expressions properly. Running this code results in the exception System.InvalidOperationException: 'variable 'hello' of type 'System.String' referenced from scope '', but it is not defined' when compiling the lambda.
Expression.Assign is a binary operation, so the variable in the left part takes a new value calculated in the right part of expression.
On these lines:
var paramExpress = (from param in paramMatcher
select Expression.Assign(Expression.Parameter(param.type, param.name),
Expression.Constant(param.value, param.type)));
Func<object> result = Expression.Lambda<Func<object>(Expression.Call(Expression.Constant(this),
method, paramExpress)).Compile();
you've received and just didn't used actual parameters values, presented in the right parts of binary expressions.
Solution:
public class C
{
public string TestValue(string hello, Guid world)
{
return hello + world;
}
public string Execute()
{
var objectParams = new Dictionary<string, object>()
{
{"hello", "somestring"},
{"world", Guid.NewGuid()}
};
var method = this.GetType().GetMethod("TestValue");
var methodParameters = method.GetParameters();
var paramMatcher = (from paramValue in objectParams
from methodParam in methodParameters
where paramValue.Key == methodParam.Name
orderby methodParam.Position // <-- preserves original order
select (name: methodParam.Name,
type: methodParam.ParameterType,
value: paramValue.Value));
var paramExpress = (from param in paramMatcher
select Expression.Assign(Expression.Parameter(param.type, param.name),
Expression.Constant(param.value, param.type)));
var values = paramExpress.Select(v => v.Right); // !!!
Func<string> result = Expression.Lambda<Func<string>>(Expression.Call(Expression.Constant(this),
method, values)).Compile();
return result.Invoke(); // returns "somestringxxxxxxx-xxxx..."
}
}
I am trying to build an expression that will call a method with an out parameter. So far I've had success, except when it comes to nullable versions of the parameters.
For this purpose lets suppose the int.TryParse(string, out int) method. I've successfully been able to build an expression (no nullables) by defining a delegate type for this purpose:
internal delegate bool TestDelegate(string input, out int value);
public static MethodInfo GetMethod()
{
return typeof(int).GetMethod("TryParse", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(string), typeof(int).MakeByRefType() }, null);
}
public static void NormalTestExpression()
{
var method = GetMethod();
var pValue = Expression.Parameter(typeof(string), "str");
var pOutput = Expression.Parameter(typeof(int).MakeByRefType(), "value");
var call = Expression.Call(method, pValue, pOutput);
var lamd = Expression.Lambda<TestDelegate>(call, pValue, pOutput);
var func = lamd.Compile();
int output;
var result = func("3", out output);
// THIS WORKS!!!
}
I am trying to make this work with nullable types. Take for example:
internal delegate bool TestNullableDelegate(string input, out int? value);
The below will fail with an Argument Exception (GetMethod() is retrieving the correct method based off of the primitive type--same method from above)
public static void WithNullableTypeFails()
{
var method = GetMethod();
var pValue = Expression.Parameter(typeof(string), "str");
var pOutput = Expression.Parameter(typeof(int?).MakeByRefType(), "value");
value
var call = Expression.Call(method, pValue, pOutput); //Argument Exception int.TryParse() doesn't accept int? argument
var lamd = Expression.Lambda<TestNullableDelegate>(call, pValue, pOutput);
var func = lamd.Compile();
}
Expression of type 'System.Nullable`1[System.Int32]' cannot be used for parameter of type 'System.Int32' of method 'Boolean TryParse(System.String, Int32 ByRef)'
Now I am aware that this is because I am still invoking the MethodInfo which is taking the primitive int type and the delegates aren't matching. So I tried the below:
public static void WithNullableTypeAttempt()
{
var method = GetMethod();
var pValue = Expression.Parameter(typeof(string), "str");
var pOutput = Expression.Parameter(typeof(int?).MakeByRefType(), "value");
var vars = Expression.Variable(typeof(int), "tmp");
var resultvar = Expression.Variable(typeof(bool), "result");
var call = Expression.Call(method, pValue, vars);
var block = Expression.Block(
Expression.Assign(vars, Expression.Constant(default(int))),
Expression.Assign(resultvar, call),
Expression.Assign(pOutput, Expression.Convert(vars, typeof(int?))),
resultvar
);
var lamd = Expression.Lambda<TestNullableDelegate>(block, pValue, pOutput);
var func = lamd.Compile(); // Invalid Operation Exception
}
I get an invalid operation exception when I attempt to compile it:
variable 'tmp' of type 'System.Int32' referenced from scope '', but it is not defined
When I open the expression in one of the expression tree visualizers I have, I see the following:
(valueToParse, value) =>
{
var tmp = 0;
var result = int.TryParse(valueToParse, out tmp);
var value = (int?)tmp;
return result;
}
So I think I am on the right track.
How can I call this method where the types vary only by the Nullable type, keeping the delegate with the Nullable type?
You are currently using an overload of the method that does not support variables.
Quoting from the reference above:
Creates a BlockExpression that contains four expressions and has no variables.
You need to tell the block expression about the variables by using an overload that supports variables like this:
var block = Expression.Block(
new ParameterExpression[] { vars, resultvar }, //variables
Expression.Assign(vars, Expression.Constant(default(int))),
Expression.Assign(resultvar, call),
Expression.Assign(pOutput, Expression.Convert(vars, typeof(int?))),
resultvar);
I wanted to create a method extending IQueryable where a user can specify in a string a property name by which he wants to distinct a collection. I want to use a logic with a HashSet.
I basically want to emulate this code:
HashSet<TResult> set = new HashSet<TResult>();
foreach(var item in source)
{
var selectedValue = selector(item);
if (set.Add(selectedValue))
yield return item;
}
using expression trees.
This is where i got so far:
private Expression AssembleDistinctBlockExpression (IQueryable queryable, string propertyName)
{
var propInfo = queryable.ElementType.GetProperty(propertyName);
if ( propInfo == null )
throw new ArgumentException();
var loopVar = Expression.Parameter(queryable.ElementType, "");
var selectedValue = Expression.Variable(propInfo.PropertyType, "selectedValue");
var returnListType = typeof(List<>).MakeGenericType(queryable.ElementType);
var returnListVar = Expression.Variable(returnListType, "return");
var returnListAssign = Expression.Assign(returnListVar, Expression.Constant(Activator.CreateInstance(typeof(List<>).MakeGenericType(queryable.ElementType))));
var hashSetType = typeof(HashSet<>).MakeGenericType(propInfo.PropertyType);
var hashSetVar = Expression.Variable(hashSetType, "set");
var hashSetAssign = Expression.Assign(hashSetVar, Expression.Constant(Activator.CreateInstance(typeof(HashSet<>).MakeGenericType(propInfo.PropertyType))));
var enumeratorVar = Expression.Variable(typeof(IEnumerator<>).MakeGenericType(queryable.ElementType), "enumerator");
var getEnumeratorCall = Expression.Call(queryable.Expression, queryable.GetType().GetTypeInfo().GetDeclaredMethod("GetEnumerator"));
var enumeratorAssign = Expression.Assign(enumeratorVar, getEnumeratorCall);
var moveNextCall = Expression.Call(enumeratorVar, typeof(IEnumerator).GetMethod("MoveNext"));
var breakLabel = Expression.Label("loopBreak");
var loopBlock = Expression.Block(
new [] { enumeratorVar, hashSetVar, returnListVar },
enumeratorAssign,
returnListAssign,
hashSetAssign,
Expression.TryFinally(
Expression.Block(
Expression.Loop(
Expression.IfThenElse(
Expression.Equal(moveNextCall, Expression.Constant(true)),
Expression.Block(
new[] { loopVar },
Expression.Assign(loopVar, Expression.Property(enumeratorVar, "Current")),
Expression.Assign(selectedValue, Expression.MakeMemberAccess(loopVar, propInfo)),
Expression.IfThen(
Expression.Call(typeof(HashSet<>), "Add", new Type[] { propInfo.PropertyType }, hashSetVar, selectedValue),
Expression.Call(typeof(List<>), "Add", new Type[] { queryable.ElementType }, returnListVar, loopVar)
)
),
Expression.Break(breakLabel)
),
breakLabel
),
Expression.Return(breakLabel, returnListVar)
),
Expression.Block(
Expression.Call(enumeratorVar, typeof(IDisposable).GetMethod("Dispose"))
)
)
);
return loopBlock;
}
I get an exception when Expression.Block is called for a variable loopBlock which goes like this:
No method 'Add' exists on type 'System.Collections.Generic.HashSet`1[T]'.
The Expression.Call method overload that you are using is for static methods.
Quoting from the reference above:
Creates a MethodCallExpression that represents a call to a static (Shared in Visual Basic) method by calling the appropriate factory method.
What you need to do is to use an overload of that method that is for calling instance methods.
Here is how the relevant part of your code would look like:
Expression.IfThen(
Expression.Call(hashSetVar, "Add", new Type[] { }, selectedValue),
Expression.Call(returnListVar, "Add", new Type[] { }, loopVar))
Notice how now we pass the instance (expression) that we need to invoke in the first parameter of Expression.Call.
Please note also that we pass an empty type parameter list. The reason for this is that the Add method in this class does not have any type parameters. The type parameter T in HashSet<T> and List<T> is defined on the class level, not on the method level.
You would need to specify the type parameters only if they are defined on the method itself like this:
void SomeMethod<T1>(...
I have a table with multiple columns of type DateTime. I want to generate dynamically the following Expression
filter= p => SqlFunctions.DateDiff("day", p.CreatedDate.Date, date) > 0;
when I know the name of column and the operator (the operator can be =, >=, <= and etc... .
Please, keep in mind that I want to ignore the time part form Datetime
Well, if you use SqlFunctions.DateDiff, that must mean that you're working in linq to entities. So I don't think you can use p.CreatedDate.Date, but you'll have to use EntityFunctions.TruncateTime(p.CreatedDate).
Anyway.
You could go for something like that (just the general idea, this should be of course improved).
assuming that p type is Employee
public static class ExpressionHelper {
//we create a Dictionary to manage the comparison operators,
//which will probably make things easier when calling the main method.
public static Dictionary<string, ExpressionType> Comparators = new Dictionary<string, ExpressionType>
{
{">", ExpressionType.GreaterThan},
{">=", ExpressionType.GreaterThanOrEqual},
{"<", ExpressionType.LessThan},
{"<=", ExpressionType.LessThanOrEqual}
};
public static Expression<Func<Employee, bool>> BuildFilterExpression(string datePart, DateTime date, string comparisonOperator)
{
var parameter = Expression.Parameter(typeof (Employee), "p");
Expression member = parameter;
//the property is a string here, it could be passed as parameter
//or managed another way
member = Expression.Property(member, "CreatedDate");
//let's find the dateDiffMethod
var dateDiffMethod = typeof (SqlFunctions).GetMethod("DateDiff", new[] {typeof (string), typeof (DateTime), typeof(DateTime)});
//same for TruncateTime method
var truncateTimeMethod = typeof (EntityFunctions).GetMethod("TruncateTime", new[] {typeof (DateTime)});
//first call truncateTime method (to keep only "Date" part)
var truncateExpression = Expression.Call(truncateTimeMethod, member);
//call dateDiff method on that
var dateDiffExpression = Expression.Call(dateDiffMethod, Expression.Constant(datePart), truncateExpression, Expression.Constant(date, typeof(DateTime?)));
//find the comparison operator
var comparator = Comparators[comparisonOperator];
//apply the comparison
var finalExpression = Expression.MakeBinary(comparator, dateDiffExpression, Expression.Constant(0, typeof(int?)));
return Expression.Lambda<Func<Employee, bool>>(finalExpression, new []{parameter});
}
}
usage, with your code would be something like that
filter = ExpressionHelper.BuildFilterExpression("day", date, ">")