I want to cast any object from ParameterExpression class.
This is sample code (and pseudo what i want to say) :
public void Execute(Expression<Action<TService>> operation)
{
try
{
var param = operation.Parameters[0]; //myparameter to cast
var obj = param as AnyCastObject; // I want to cast
DoSomething(obj); // and do something this object without losing any assigned propery.
operation.Compile().Invoke(OpenNewChannel);
}
finally
{
CloseChannel();
}
}
Edit:
This is my method body:
Execute(x => x.UserAuthentication(requestDto));
I want to manipulate requestDto.
Here it is... With this you should be able to extract your requestDto.
Note that you don't need the Invoke() when you call compiled expressions.
operation.Compile()(OpenNewChannel);
is enough.
Now, to extract the requestDto:
// Works for Execute(x => x.UserAuthentication(something))
// where something must be a constant value, a variable,
// a field, a property
var param = operation.Parameters[0]; //myparameter to cast
var body = operation.Body as MethodCallExpression;
if (body == null || body.Object != param || body.Method.Name != "UserAuthentication")
{
throw new NotSupportedException();
}
// If you have a type for the parameter, replace it here:
// object -> yourtype
object requestValue;
var constantExpression = body.Arguments[0] as ConstantExpression;
if (constantExpression == null)
{
// For nearly all the types of expression, the only way
// to extract the final value is to compile them and then
// execute them (the execution is the last "()" )
// If you have a type for the parameter, replace it here:
// Func<object> -> Func<yourtype>
requestValue = Expression.Lambda<Func<object>>(body.Arguments[0]).Compile()();
}
else
{
// Constant expression values can be directly extracted
// If you have a type for the parameter, replace it here:
// (yourtype)constantExpression.Value
requestValue = constantExpression.Value;
}
Related
I would like to know if it is possible to create an instance of a type known only at runtime and assign values to the properties of this instance by using compiled expressions and if so how it is to be done.
I have a generic class with a method which accepts an instance of T and returns a copy. T is only known at runtime or rather is user/consumer defined. I know how to do so with reflection (assuming it has an empty constructor in this example and without exception handling or null checks for simplification).
public class MyClass<T>
{
public T CreateCopy(T source)
{
var type = typeof(T);
var copy = type.GetConstructor(Type.EmptyTypes).Invoke(null);
foreach(var pi in type.GetProperties())
{
pi.SetValue(copy, pi.GetValue(source));
}
return copy;
}
}
Reflection is quite expensive and after some digging i found an option to at least create an instance of T with compiled expressions.
var type = typeof(T);
Expression.Lambda<Func<T>>(Expression
.New(type.GetConstructor(Type.EmptyTypes)
?? throw new InvalidOperationException(
$"Type has to have an empty public constructor. {type.Name}")))
.Compile();
After some benchmarking i have found out that it performs around 6 times faster than the CreateCopy(...) method. The thing is that i do not know which type will be passed in as a generic and how many properties it will have.
Is there a way to do all of the operations from CreateCopy(...) method with compiled expressions?
Ihave looked into Expression.Asign, Expression.MemberInit but i am not able to find anything appropriate. The problem with Expression.MemberInit ist that it expects to have an Expresssion.Bind and Expression.Constant but i cant get the values of the properties from the passed instance of T into it. Is there a way?
Thank you.
P.S. So i am looking for something like:
var type = typeof(T);
var propertyInfos = type.GetProperties();
var ctor = Expression.New(type.GetConstructor(Type.EmptyTypes));
var e = Expression.Lambda<Func<T, T>>(Expression
.MemberInit(ctor, propertyInfos.Select(pi =>
Expression.Bind(pi, Expression.Constant(pi.GetValue(source)))))).Compile();
You are almost there. What you need is to define a parameter and then assign the properties with property access expression like below :
public static T Express<T>(T source)
{
var parameter = Expression.Parameter(typeof(T), "source");
var type = typeof(T);
var ctor = Expression
.New(type.GetConstructor(Type.EmptyTypes));
var propertyInfos = type.GetProperties();
var e = Expression
.Lambda<Func<T, T>>(
Expression
.MemberInit(ctor, propertyInfos.Select(pi =>
Expression.Bind(pi, CanBeAssigned(pi.PropertyType)
? (Expression)Expression.Property(parameter, pi.Name)
: Expression.Call(typeof(Program).GetMethod(nameof(Express))
.MakeGenericMethod(pi.PropertyType),
Expression.Property(parameter, pi.Name)
))
)),
parameter
);
var x = e.Compile();
var z = x(source);
return z;
}
public static bool CanBeAssigned(Type t)
{
return t.IsValueType || t.Name == "String"; // this might need some improvements
}
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));
}
I'm following this SO answer to convert lambda expressions to partial SQL syntax.
However, I have problems parsing the expression for Contains. I added a method:
private bool ParseContainsExpression(MethodCallExpression expression)
{
MemberExpression member = (MemberExpression)expression.Arguments[0];
var methodInfo = typeof(List<int>).GetMethod("Contains", new Type[] { typeof(int) });
//TODO check if list contains value
return false;
}
As I'm totally new to expressions, I don't know from where get the property name and value, and the list that contains the values I want to check against. Where are these properties and values stored within the expression?
Your implementation is quite different from the example answer. You really need to inherit from ExpressionVisitor so that you can properly parse the tree.
Let's take this expression for an example:
var myList = new List<string> { "A" };
Expression<Func<string, bool>> a = (s) => myList.Contains(s);
ParseContainsExpression(a.Body as MethodCallExpression);
private bool ParseContainsExpression(MethodCallExpression expression)
{
expression.Object; //myList
expression.Arguments[0]; //s
return false;
}
Note however, these are still Expressions, they are not actual values yet. You need to invoke the expression to get the values.
However, in our case - myList is actually a ConstantExpression. So we can do this:
((expression.Object as MemberExpression).Expression as ConstantExpression).Value; //myList
Which returns us the original list. Note that it's a field access, because the expression is compiled into a closure, which puts myList as a field in the closure class. As you can see, we have to make a lot of assumptions as to the type of Expression we're handling (that it's a field access and then a constant expression). You really need a fully-fledged visitor implementation to do this properly (which the linked answer describes)
Basically you need to process the Method, Object(expression that represents the instance for instance method calls or null for static method calls) and Arguments(a collection of expressions that represent arguments of the called method) properties of the MethodCallExpression class.
Specifically for Contains, you need to avoid (or process differently if needed) the string.Contains method, and also handle static methods like Enumerable.Contains as well as instance methods like ICollection<T>.Contains, List<T>.Contains etc. In order to get the list values (when possible), you have to find some sort of a constant expression.
Here is a sample:
private bool ParseContainsExpression(MethodCallExpression expression)
{
// The method must be called Contains and must return bool
if (expression.Method.Name != "Contains" || expression.Method.ReturnType != typeof(bool)) return false;
var list = expression.Object;
Expression operand;
if (list == null)
{
// Static method
// Must be Enumerable.Contains(source, item)
if (expression.Method.DeclaringType != typeof(Enumerable) || expression.Arguments.Count != 2) return false;
list = expression.Arguments[0];
operand = expression.Arguments[1];
}
else
{
// Instance method
// Exclude string.Contains
if (list.Type == typeof(string)) return false;
// Must have a single argument
if (expression.Arguments.Count != 1) return false;
operand = expression.Arguments[0];
// The list must be IEnumerable<operand.Type>
if (!typeof(IEnumerable<>).MakeGenericType(operand.Type).IsAssignableFrom(list.Type)) return false;
}
// Try getting the list items
object listValue;
if (list.NodeType == ExpressionType.Constant)
// from constant value
listValue = ((ConstantExpression)list).Value;
else
{
// from constant value property/field
var listMember = list as MemberExpression;
if (listMember == null) return false;
var listOwner = listMember.Expression as ConstantExpression;
if (listOwner == null) return false;
var listProperty = listMember.Member as PropertyInfo;
listValue = listProperty != null ? listProperty.GetValue(listOwner.Value) : ((FieldInfo)listMember.Member).GetValue(listOwner.Value);
}
var listItems = listValue as System.Collections.IEnumerable;
if (listItems == null) return false;
// Do whatever you like with listItems
return true;
}
I have built my own SQL Query builder that breaks apart an Expression, however, I'm having an issue trying to get the value of string defined in the same function as the lambda expression.
Here is what I am trying to do in console app:
private static void MyBuilderTest()
{
var sqlBuilder = new SqlBuilder();
// Doesn't work -- NEED GUIDANCE HERE
var testValue = "Test"; // Defined in the same function as the lambda below
sqlBuilder.Select<FooObject>(o => o.FooValue == testValue);
// Works
var someObject = new SomeObject { SomeValue = "classTest };
sqlBuilder.Select<FooObject>(o => o.FooValue == someObject.SomeValue);
}
In my builder it subclasses from ExpressionVisitor, and I override the VisitMember. I found that a string defined in at the base Console level will come back as:
Node.Expression.NodeType == ExpressionType.Constant
The Node.Expression passes back properties of:
CanReduce = false
DebugView = ".Constant<ConsoleApplication1.Program+<>c__DisplayClass1>(ConsoleApplication1.Program+<>c__DisplayClass1)"
NodeType = Constant
Type = System.Type {System.RunetimeType}
Value = {ConsoleApplication1.Program}
The Node.Expression.Value contains:
testValue = "Test" (Type: string)
How do I get this value? I've tried several things, like:
var memberType = node.Expression.Type.DeclaringType;
This passes back a ConsoleApplication1.Program type.
However, when I do:
memberType.GetProperty("testValue"); // Declaring Type from Expression
It passes back null.
The above methods work fine if I place the lambda "strings" in a class, but doesn't work if they string is defined in the console function.
Can anyone tell me how to get the string value if it's defined at the function level of the lambda?
EDITED: Added VisitMember
protected override Expression VisitMember(MemberExpression node)
{
if (node.NodeType == ExpressionType.Constant)
{
// Node.Expression is a ConstantExpression type.
// node.Expression contains properties above
// And Has Value of: {ConsoleApplication1.Program}
// Expanding Value in Watch window shows: testValue = "Test"
// How do I get this value, if the ConsoleApplication1.Program type doesn't
// even know about it? Looks like maybe a dynamic property?
}
}
EDITED
Added code to the console app example to show what works and what doesn't.
The lambda in your example has "closed over" the testValue variable, meaning the compiler has captured it as a field of the same name in an automatically generated class called ConsoleApplication1.Program+<>c__DisplayClass1>. You can use normal reflection to get the current value of that field by casting the right hand-side of the binary expression into a MemberExpression.
var testValue = "hello";
var expr = (Expression<Func<string, bool>>) (x => x == testValue);
var rhs = (MemberExpression) ((BinaryExpression) expr.Body).Right;
var obj = ((ConstantExpression) rhs.Expression).Value;
var field = (FieldInfo) rhs.Member;
var value = field.GetValue(obj);
Debug.Assert(Equals(value, "hello"));
testValue = "changed";
value = field.GetValue(obj);
Debug.Assert(Equals(value, "changed"));
Alternatively you can change your variable into a constant.
const string testValue = "hello";
var expr = (Expression<Func<string, bool>>) (x => x == testValue);
var value = ((ConstantExpression) ((BinaryExpression) expr.Body).Right).Value;
Debug.Assert(Equals(value, "hello"));
Instead of doing this by yourself, have a look at PartialEvaluator from Matt Warren. It replaces all references to constants with the constants themselves.
I am working on a function that takes a delegate type and a delegate of type Action<Object[]> and creates a dynamic function of the given type, that passes, if called, all arguments to the given action handle:
public static T GetEventDelegate<T>(Action<Object[]> handler) where T : class
{
if (!typeof(T).IsSubclassOf(typeof(Delegate)))
throw new Exception("T must be a delegate type");
Type[] argTypes = typeof(T).GetMethod("Invoke").GetParameters().Select((para) => para.ParameterType).ToArray();
List<ParameterExpression> lstArgs = new List<ParameterExpression>(
argTypes.Select((arg)=>Expression.Parameter(arg))
);
ParameterExpression result = Expression.Variable(typeof(Object[]));
var assignExpression = Expression.NewArrayInit(typeof(Object),lstArgs.ToArray());
var callExpression = Expression.Call(handler.Method, result);
var block = Expression.Block(
new List<ParameterExpression>(){result},
new Expression[]{assignExpression,callExpression}
);
var del = Expression.Lambda(block, lstArgs.ToArray()).Compile();
return Delegate.CreateDelegate(typeof(T), del, "Invoke") as T;
}
private static void testDel()
{
var del = GetEventDelegate<EventHandler>(
(x) =>
{
//Error, because x == null
Debug.Print(x.ToString());
}
);
del("testString", new EventArgs());
}
Unfortunately the action handler only gets a null value passed (see testDel()).
Could you please help me to find the error?
Thanks in advance!
Your problem is because you initialize the result ParameterExpression as type Object, but never actually assign it a value. What this pretty much compiles down to (after calling compile) is:
void Func(arguments..)
{
Object result;
new object[](arguments...);
method(result);
}
You never actually assign the array to the result with an assign expression.
Unrelated to your problem, you could use Expression.Lambda() when creating the lambda because you have the type from the generic.