I have a collection of methods, and I'd like to identify any that contain a Func<(,,,)> parameter (ideally through reflection).
This would be simple enough by tagging such parameters with [IsFuncAttribute], but I'd like to avoid that approach if possible.
For example, if I had the following method, how could I reliably determine that the third parameter is a Func<,>?
public T MyMethod<T>(T param1, bool param2, Func<t,bool> param3)
{
// do something
}
Alternatively, being able to identify the third parameter as a delegate with a non void return type would be equally useful.
MethodInfo methodInfo = ...;
ParameterInfo[] parameters = methodInfo.GetParameters();
bool thirdParameterIsFunc =
parameters.Length >= 3 &&
parameters[2].ParameterType.IsGenericType &&
parameters[2].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>));
DotNetFiddle
This is specifically for Func<,>. If you want to match any sort of Func, with any number of parameters, then you'll either need a list of typeof(Func<>), typeof(Func<,>), typeof(Func<,,>), etc, or you'll need to match on the full name of the type.
If you need to match a Func with any number of parameters, as an alternative to explicitly testing against every version of Func (There are 17 of them), you could use something like this extension method (Note that this particular example is specific to .NET Core/.Net 5.0):
public static bool IsAnyFunc(this Type type)
{
int typeParameterCount = type.GenericTypeArguments.Length;
if (typeParameterCount == 0)
{
return false;
}
Type funcType = typeof(Func<>)
.Assembly
.GetTypes()
.FirstOrDefault(t =>
t.Name.StartsWith("Func`")
&& t.GetTypeInfo().GenericTypeParameters.Length == typeParameterCount);
return funcType == null ? false : type.GetGenericTypeDefinition() == funcType;
}
Edit:
Although, if you're going to be making this check frequently, the above may not be performant enough. In which case you may want to explicitly list every possilbe Func. However, you don't actually need to check against them all, if you index them by parameter count:
private static readonly List<Type> FuncTypes = new()
{
typeof(Func<>),
typeof(Func<,>),
typeof(Func<,,>),
typeof(Func<,,,>),
typeof(Func<,,,,>),
typeof(Func<,,,,,>),
typeof(Func<,,,,,,>),
typeof(Func<,,,,,,,>),
typeof(Func<,,,,,,,,>),
typeof(Func<,,,,,,,,,>),
typeof(Func<,,,,,,,,,,>),
typeof(Func<,,,,,,,,,,,>),
typeof(Func<,,,,,,,,,,,,>),
typeof(Func<,,,,,,,,,,,,,>),
typeof(Func<,,,,,,,,,,,,,,>),
typeof(Func<,,,,,,,,,,,,,,,>),
typeof(Func<,,,,,,,,,,,,,,,,>)
};
public static bool IsAnyFunc(this Type type)
{
int typeParameterCount = type.GenericTypeArguments.Length;
if (typeParameterCount < 1 || typeParameterCount > FuncTypes.Count)
{
return false;
}
return type.GetGenericTypeDefinition() == FuncTypes[typeParameterCount - 1];
}
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 am trying to implement the Queryable interface and I would like to extract the type from the expression.
I have implemented the Queryable using the sample in http://blogs.msdn.com/b/mattwar/archive/2007/07/30/linq-building-an-iqueryable-provider-part-i.aspx.
When I reach the Execute method in my Provider, the expression is :
expression = {value(TestApp.HouseQueryable`1[TestApp.House]).OfType().Where(i => (i.Address == "N.125 Oxford St."))}
The first argument seem to be the type but from there on I am not sure how to extract it from the OfType() method. Can someone help me on this ?
The code to build the queryable and the query provider are the one from the tutorial.
Thanks
Edit: To expand more on my goal, I would like to query from different services depending on the type given. I know that it is not the best way to do it since i will end up having a big IF ELSE in my query provider.
By following the response of Ani, I inherited from ExpressionVisitor and checked for method call to extract the type. I only tested my scenario which is having only one .OfType() and it seems to work.
public class ExpressionTreeModifier : ExpressionVisitor
{
public ExpressionTreeModifier(Expression expression)
{
this.Visit(expression);
}
protected override Expression VisitMethodCall(MethodCallExpression methodCall)
{
var method = methodCall.Method;
if (method.Name == "OfType" && method.DeclaringType == typeof(Queryable))
{
var type = method.GetGenericArguments().Single();
OfType = (Type)type;
}
return base.VisitMethodCall(methodCall);
}
public Type OfType { get;set; }
}
I'm not sure where you're going with this exactly, but to answer what you ask (and nothing more):
IQueryable queryable = ...
var methodCall = queryable.Expression as MethodCallExpression;
if(methodCall != null)
{
var method = methodCall.Method;
if(method.Name == "OfType" && method.DeclaringType == typeof(Queryable))
{
var type = method.GetGenericArguments().Single();
Console.WriteLine("OfType<{0}>", type);
}
}
Not sure how this highly specific bit of code is going to help you write your own query-provider, honestly. Could you expand a bit on your broader goals?
I have something like this:
public Expression<Func<Message, bool>> FilterData()
{
switch (this.operatorEnum)
{
case FilterParameterOperatorEnum.EqualTo:
return message => !string.IsNullOrEmpty(message.Body) &&
message.Body
.Equals(this.value, StringComparison.InvariantCultureIgnoreCase);
case FilterParameterOperatorEnum.NotEqualTo:
return message => !string.IsNullOrEmpty(message.Body) &&
!message.Body
.Equals(this.value, StringComparison.InvariantCultureIgnoreCase);
case FilterParameterOperatorEnum.Contains:
return message =>
!string.IsNullOrEmpty(message.Body) &&
message.Body.IndexOf(this.value,
StringComparison.InvariantCultureIgnoreCase) >= 0;
case FilterParameterOperatorEnum.DoesNotContain:
return message =>
!string.IsNullOrEmpty(message.Body) &&
message.Body.IndexOf(this.value,
StringComparison.InvariantCultureIgnoreCase) == -1;
}
}
As you can see this is done on Message.Body
I now what to do the same thing on other string properties on the Message class and I don't want to duplicate all that code.
Is there a way to do that by passing in the property somehow?
Eclude the expression that retrieves the property value into a separate lambda expression:
public Expression<Func<Message, bool>> FilterData(Func<Message, string> retrievePropValueFunc)
In your filter expressions, you can then call that new lambda expression (just showing one as an example):
return message => !string.IsNullOrEmpty(retrievePropValueFunc(message))
&& retrievePropValueFunc(message)
.Equals(this.value, StringComparison.InvariantCultureIgnoreCase);
To get to the Body property, pass message => message.Body to the retrievePropValueFunc parameter; as you see, you can modify this to pass different lambda expressions for the retrieval of other properties just as well.
You can try composing the expression completely manually, which will give you the ability to specify the property name as a string. This isn't a complete solution and it's untested, but it may give you a starting point to see what I mean:
var parameter = Expression.Parameter(typeof(Message), "o");
var getname = Expression.Property(parameter, "Body");
var isnullorempty = Expression.Not(Expression.Call(typeof(string), "IsNullOrEmpty", null, getname));
var compare = Expression.Equal(getname, Expression.Constant("thisvalue"));
var combined = Expression.And(isnullorempty, compare);
var lambda = Expression.Lambda(combined, parameter);
So you would change your function to accept "Body" as a parameter, and then cast the lambda at the end to:
expression<func<Message, bool>>
I may not have got the syntax for creating the lambda exactly right though.
Just change your function to receive the property instead of the message.
Or the hard way pass one more parameter for the property name and select it using reflection.
Edit just for one option than change for all the options.
public Func<Message, string, bool> FilterData()
{
return (message, propName) =>
{
var prop = message.GetType().GetProperty(propName);
if(prop != null){
var propValue = (string)prop.GetValue(message,null);
return !string.IsNullOrEmpty(propValue) && ...;
}
return false;
};
}
A very quick and dirty approach could be an enum plus some kind of magic:
public enum FilterTarget { Body, AnyOtherProp };
public Expression<Func<Message, bool>> FilterData(FilterTarget filterTarget)
{
string toBeFiltered = string.Empty;
switch(filterTarget)
{
case FilterTarget.Body :
{ toBeFiltered = message.Body; } break;
case FilterTarget.AnyOtherProp :
{ toBeFiltered = message.AnyOtherProp; } break;
default:
{
throw new ArgumentException(
string.Format("Unsupported property {0}", filterTarget.ToString()
);
}
}
switch (this.operatorEnum)
{
case FilterParameterOperatorEnum.EqualTo:
return message => !string.IsNullOrEmpty(toBeFiltered) &&
toBeFiltered.Equals(this.value, StringComparison.InvariantCultureIgnoreCase);
/* CUT: other cases are similar */
}
}
You can make the FilterData method even more fancy letting it accept a params FilterTarget[] thus gaining multi-filtering capabilities (off the top of my head, code not included).
Usage:
var aFilterDataResult = FilterData(FilterTarget.Body);
var anotherFilterDataResult = FilterData(FilterTarget.AnyOtherProp);
...
I have had to find a way to substitute implicit field references in a lambda expression with it's real value. For example :
Expression<Func<TestObject, String>> exp = null;
for (int i = 0; i < 1; i++)
{
exp = t => t.SubObjs[i].TestSTR;
}
Func<TestObject, String> testFunc = exp.Compile();
String testValue = testFunc(myObj);
When inspecting the delegate, you can see this :
{t => t.SubObjs.get_Item(value(testExpression.Program+<>c__DisplayClass4).i).TestSTR}
When calling the delegate outside the for loop, the value of "i" is solved, by reference. But "i" have changed since it's last iteration ("i" == 1 and not 0).
So I build a specific ExpressionVisitor in order to replace the corresponding node with a ConstantExpression :
public class ExpressionParameterSolver : ExpressionVisitor
{
protected override Expression VisitMember(MemberExpression node)
{
if (node.ToString().StartsWith("value(") && node.NodeType == ExpressionType.MemberAccess)
{
var index = Expression.Lambda(node).Compile().DynamicInvoke(null);
return Expression.Constant(index, index.GetType());
}
return base.VisitMember(node);
}
}
I don't have found a way other than .StartsWith("value(") in order to detect that the current node is a reference to a field... this kind of node inherits from FieldExpression but this class is internal, and I'm not sure FieldExpression only encapsulate what I consider an "implicit field reference".
So is there a way (an attribute or a method) to explicitly know that a MemberExpression node is an implicit field reference ???
Thanks in advance !!!
and thanks to this stakx post
Just fetch the Member property from the expression and see whether it's a FieldInfo...
If you only want it to be for cases where the class is compiler-generated you could use
if (expression.Member is FieldInfo &&
expression.Member
.DeclaringType
.IsDefined(typeof(CompilerGeneratedAttribute), false))
{
....
}
There can be other reasons why a type might be compiler-generated though. It doesn't sound like a terribly good idea to me.
Can't you just avoid capturing loop variables in your lambda expressions to start with?