Reputation: 746
I know I can define a generic function and then call it with Type parameters by help of reflection. Something like that:
private static void SomeFunc<Tf>()
{
// Something type safe against Tf
}
public void CallingFunc()
{
var someType = typeof(whatever); // This may be retrieved using reflection as well instead
var someMethod = this.GetType().GetMethod(nameof(SomeFunc)), BindingFlags.Static | BindingFlags.NonPublic);
var typedMethod = someMethod.MakeGenericMethod(someType);
typedMethod.Invoke(null, null);
}
Now, is there a way to declare this SomeMethod<Tf>()
inline as a lambda, to avoid declaring it as a separate method in the class, but still being able to use it with MakeGenericMethod
? Something like:
public void CallingFunc()
{
var someType = typeof(whatever); // This may be retrieved using reflection as well instead
// This obviously doesn't work as it requires Tf to be known at this stage, and we can have Tf only as a Type variable here => what should I do instead?
var someMethod = new Action<Tf>(() => /* Something type safe against Tf */).Method;
var typedMethod = someMethod.MakeGenericMethod(someType);
typedMethod.Invoke(null, null);
}
Making CallingFunc
generic as well is not an option - the someType
variable is retrieved with reflection so is not known on compile time.
Upvotes: 0
Views: 359
Reputation: 11
Based on: https://gist.github.com/afreeland/6733381 with a little improvement:
public class ExpressionBuilder
{
// Define some of our default filtering options
private static MethodInfo containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
private static MethodInfo startsWithMethod = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
private static MethodInfo endsWithMethod = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
public static Expression<Func<T, bool>> GetExpression<T>(List<GridHelper.Filter> filters)
{
// No filters passed in #KickIT
if (filters.Count == 0)
return null;
// Create the parameter for the ObjectType (typically the 'x' in your expression (x => 'x')
// The "parm" string is used strictly for debugging purposes
ParameterExpression param = Expression.Parameter(typeof(T), "parm");
// Store the result of a calculated Expression
Expression exp = null;
if (filters.Count == 1)
exp = GetExpression<T>(param, filters[0]); // Create expression from a single instance
else if (filters.Count == 2)
exp = GetExpression<T>(param, filters[0], filters[1]); // Create expression that utilizes AndAlso mentality
else
{
// Loop through filters until we have created an expression for each
while (filters.Count > 0)
{
// Grab initial filters remaining in our List
var f1 = filters[0];
var f2 = filters[1];
// Check if we have already set our Expression
if (exp == null)
exp = GetExpression<T>(param, filters[0], filters[1]); // First iteration through our filters
else
exp = Expression.AndAlso(exp, GetExpression<T>(param, filters[0], filters[1])); // Add to our existing expression
filters.Remove(f1);
filters.Remove(f2);
// Odd number, handle this seperately
if (filters.Count == 1)
{
// Pass in our existing expression and our newly created expression from our last remaining filter
exp = Expression.AndAlso(exp, GetExpression<T>(param, filters[0]));
// Remove filter to break out of while loop
filters.RemoveAt(0);
}
}
}
return Expression.Lambda<Func<T, bool>>(exp, param);
}
private static Expression GetExpression<T>(ParameterExpression param, GridHelper.Filter filter)
{
// The member you want to evaluate (x => x.FirstName)
MemberExpression member = Expression.Property(param, filter.PropertyName);
var typeOfValue = filter.Value.GetType();
// The value you want to evaluate
ConstantExpression constant = Expression.Constant(filter.Value, typeOfValue);
// Determine how we want to apply the expression
switch (filter.Operator)
{
case GridHelper.Operator.Equals:
return Expression.Equal(member, constant);
case GridHelper.Operator.NotEqual:
return Expression.Equal(member, constant);
case GridHelper.Operator.Contains:
return Expression.Call(member, containsMethod, constant);
case GridHelper.Operator.GreaterThan:
return Expression.GreaterThan(member, constant);
case GridHelper.Operator.GreaterThanOrEqual:
return Expression.GreaterThanOrEqual(member, constant);
case GridHelper.Operator.LessThan:
return Expression.LessThan(member, constant);
case GridHelper.Operator.LessThanOrEqualTo:
return Expression.LessThanOrEqual(member, constant);
case GridHelper.Operator.StartsWith:
return Expression.Call(member, startsWithMethod, constant);
case GridHelper.Operator.EndsWith:
return Expression.Call(member, endsWithMethod, constant);
}
return null;
}
private static BinaryExpression GetExpression<T>(ParameterExpression param, GridHelper.Filter filter1, GridHelper.Filter filter2)
{
Expression result1 = GetExpression<T>(param, filter1);
Expression result2 = GetExpression<T>(param, filter2);
return Expression.AndAlso(result1, result2);
}
}
// Filter class
public class GridHelper
{
public enum Operator
{
Contains,
GreaterThan,
GreaterThanOrEqual,
LessThan,
LessThanOrEqualTo,
StartsWith,
EndsWith,
Equals,
NotEqual,
Or
}
public class Filter
{
public string PropertyName { get; set; }
public object? Value { get; set; }
public Operator Operator { get; set; }
}
}
Upvotes: -1