简体   繁体   中英

Define a generic lambda in C# and assign it to a variable to be used with MakeGenericMethod

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.

Based on: https://gist.github.com/afreeland/6733381 thanks !

With a little improve:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM