Creating DistinctBy using Expression trees - c#

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>(...

Related

How to get expression for a local variable

I am making a framework that supports plugins, therefore it load classes defined in plugin dlls when the application starts.
foreach (Type t in assembly.GetTypes()) {
if (t.IsAssignableTo(typeof(AIGameBodyBehaviour))) {
AIGameBodyBehaviourFactory factory = new AIGameBodyBehaviourFactory() {
Id = t.Name,
NewAIGameBodyBehaviour = Expression.Lambda<Func<AIGameBodyBehaviourFactoryArguments, AIGameBodyBehaviour>>(
Expression.New(
t.GetConstructor(new Type[] {typeof(AIGameBodyBehaviourFactoryArguments) }),
new Expression[] {
//stuck at here
}
),
new ParameterExpression[] {
Expression.Parameter(typeof(AIGameBodyBehaviourFactoryArguments), "arguments")
}
).Compile()
};
EntityLoaderInterface.AddAIGameBodyBehaviourFactory(factory);
}
}
As shown above, the complied lambda takes a parameter of type AIGameBodyBehaviourFactoryArguments and return an instance of AIGameBodyBehaviour. The body of the lambda function is a call to the constructor of the subclass of AIGameBodyBehaviour defined by user.
The parameter of the lambda expression is named "arguments". This name should be reused as the expression repressenting the value to be passed into the constructor. However, I cannot findout how to do this even after examining the documentation provided by Microsoft and some research via Google.
When you are stuck with expressions, you can consider SharpLab. If I understood you correctly, you want to have an expression that looks like this:
Expression<Func<AIGameBodyBehaviourFactoryArguments, AIGameBodyBehaviour>> expression =
(arguments) => new AIGameBodyBehaviour(arguments);
SharpLab will show you that the compiler will turn this line into
ParameterExpression parameterExpression = Expression.Parameter(typeof(AIGameBodyBehaviourFactoryArguments), "arguments");
ConstructorInfo constructor = (ConstructorInfo)MethodBase.GetMethodFromHandle((RuntimeMethodHandle)/*OpCode not supported: LdMemberToken*/);
Expression[] array = new Expression[1];
array[0] = parameterExpression;
NewExpression body = Expression.New(constructor, (IEnumerable<Expression>)array);
ParameterExpression[] array2 = new ParameterExpression[1];
array2[0] = parameterExpression;
Expression<Func<AIGameBodyBehaviourFactoryArguments, AIGameBodyBehaviour>> expression = Expression.Lambda<Func<AIGameBodyBehaviourFactoryArguments, AIGameBodyBehaviour>>(body, array2);
(See Sharplab)
You can see that the same instance for ParameterExpression is used as the second parameter in Expression.New and in the second parameter in Expression.Lambda. Hence your code should look like this:
foreach (Type t in assembly.GetTypes()) {
if (t.IsAssignableTo(typeof(AIGameBodyBehaviour))) {
var parameterExpression = Expression.Parameter(typeof(AIGameBodyBehaviourFactoryArguments), "arguments");
AIGameBodyBehaviourFactory factory = new AIGameBodyBehaviourFactory() {
Id = t.Name,
NewAIGameBodyBehaviour = Expression.Lambda<Func<AIGameBodyBehaviourFactoryArguments, AIGameBodyBehaviour>>(
Expression.New(
t.GetConstructor(new Type[] {typeof(AIGameBodyBehaviourFactoryArguments) }),
new Expression[] {
parameterExpression
}
),
new ParameterExpression[] {
parameterExpression
}
).Compile()
};
EntityLoaderInterface.AddAIGameBodyBehaviourFactory(factory);
}
}

Is there a way to instantiate a class via Expression.New where 'ConstructorInfo' is supplied by an ExpressionParameter?

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.

convert list of Column names to select Lambda expression [duplicate]

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();

How to create parameter which should implement multiple interfaces in expression tree?

I want to create parameter (instance of ParameterExpression class), which should implement multiple interfaces (let they are IComparable, IFormattable) like that:
//TypeWithIComparableIFormattable composite_type = ...;
ParameterExpression parameter = Parameter(composite_type, "composite_param");
// Usage specific methods of composite param from implemented interfaces
Despite the fact that a variable/parameter can be an instance of different types, you cannot delcare it like this:
IComparable, IFormattable obj;
To do multi-interface implementation check, you may:
Delcare your own interface, say IComparableAndFormattable, inheriting from IComparable and IFormattable. But this approach requires that the type of parameter must implement the interface.
Perform a runtime check. It's wordy, but keeps the code staying flexible:
Use Expression.TypeAs() method to convert your parameter to the desired type.
var param = Expression.Parameter(typeof(object), "o");
// IComparable comparable;
var comparableDeclare = Expression.Parameter(typeof(IComparable), "comparable");
// comparable = o as IComparable;
var comparableAssign = Expression.Assign(comparableDeclare, Expression.TypeAs(param, typeof(IComparable)));
// if (comparable == (IComparable)null)
// {
// throw new ArgumentException("The parameter must be a instance of IComparable.", nameof(o));
// }
var comparableCheck = Expression.IfThen(Expression.Equal(comparableDeclare, Expression.Constant(null, typeof(IComparable))),
ThrowNotTypeOf(typeof(IComparable), param.Name));
var formattableDeclare = Expression.Parameter(typeof(IFormattable), "formattable");
// formattable = o as IFormattable;
var formattableAssign = Expression.Assign(formattableDeclare, Expression.TypeAs(param, typeof(IFormattable)));
// if (formattable == (IFormattable)null)
// {
// throw new ArgumentException("The parameter must be a instance of IFormattable.", nameof(o));
// }
var formattableCheck = Expression.IfThen(
Expression.Equal(formattableDeclare, Expression.Constant(null, typeof(IFormattable))),
ThrowNotTypeOf(typeof(IFormattable), param.Name));
var block = Expression.Block(
new [] {
comparableDeclare, formattableDeclare
}, // local variables
comparableAssign, comparableCheck, formattableAssign, formattableCheck);
foreach (var exp in block.Expressions)
{
Console.WriteLine(exp);
}
// Compile the expression tree
var method = Expression.Lambda<Action<object>>(block, param).Compile();
method.Invoke(new ComparableFormattable());
where ThrowNotTypeOf is a helper method generating a throw new ArgumentExceptionstatement:
private static Expression ThrowNotTypeOf(Type type, string paramName)
{
var ctor = typeof(ArgumentException).GetConstructor(new[] { typeof(string), typeof(string) });
Debug.Assert(ctor != null);
var messageArg = Expression.Constant($"The parameter must be an instance of '{type.Name}'.");
var paramArg = Expression.Constant(paramName);
return Expression.Throw(Expression.New(ctor, messageArg, paramArg));
}

Creating Expression with out parameter and nullable types

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);

Categories

Resources