Identify Func<> parameter in method using reflection - c#
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];
}
Related
How to build expression tree for Contains<T>
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; }
Default value and linq
Why this simple code doesnt work? var abc = new[]{1,2,3,4}.Select(x => default(typeof(x))); i expect something like array of zeros but get compiler error. So how i can default values in lambda expressions? In my real application i have meta class with type Type public class FieldInfo { public Type type } I get IEnumerable<FieldInfo> as parameter to my method and want return an array of default values for each type (object[])
This is the problem: typeof(x) You're using the typeof operator as if it's a method call, taking a value. It's not. It needs a compile-time type name or type parameter (or unbound type such as Dictionary<,>, or void). The same is try with the default operator. So that's what's wrong - but without knowing what you're trying to achieve, we can't actually advise you on how to fix it. You might just want something like this: public static IEnumerable<T> SelectDefault<T>(this IEnumerable<T> source) { return source.Select(x => default(T)); } Then this: var abc = new[]{1,2,3,4}.SelectDefault().ToArray(); ... would give you an int[] with all values zero. EDIT: Now we have more information, it's easier to provide what you want, which is basically a method to get the default value from a Type, boxing as appropriate: public static object GetDefaultValue(Type type) { // Non-nullable value types should be boxed, basically if (type.IsValueType && Nullable.GetUnderlyingType(type) == null) { // Not the swiftest approach in the world, but it works... return Activator.CreateInstance(type); } // Everything else defaults to null return null; } So you'd have something like: var defaults = fields.Select(field => GetDefaultValue(field.type)); (Where field is a FieldInfo as per the question - I'm deliberately not calling GetType() here.)
Do you mean this? var abc = new[] { 1, 2, 3, 4 } .Select(x = > { var xType = x.GetType(); if (xType.IsValueType) { return Activator.CreateInstance(xType); } else { return null; } })
You can't get the default value at runtime this way, default() and typeof() are compile time features. But you can try this suggestion. It uses Activator.CreateInstance to get the default value for a value type at runtime.
The compiler needs a compile-time type: var abc = new[]{1,2,3,4}.Select(x => default(Int32)); will generate all 0's. I have no idea what you are trying to achieve with this, though, because all this does is return an empty array with the default value.
Call a function with unknown type and number of parameters in C#
I am writing a library in C# and I have to call some methods defined by other programmers in their classes, so I do not priory know about types and number of parameters. For example, Class exampleClass{ void method1(int param1, double param2){...} bool method2(){...} object method3(string param1){....} } In my program, I want to call these methods. As I don't know their parameters and return types, I cannot use "delegate"s (which have known types and parameters) but in run-time, I can use, for example, reflection to extract methods and their parameters ("MethodInfo"s) from the class but how to use this information to call the methods? (assuming that I can generate the proper values to be used as the parameters of methods). Thanks PS: I know "params object []" approach but it will force programmers to use "params" objects instead of defining their usual parameters in their methods. So, I don't want to use this approach.
You can use reflection to get all the information you need about a method. For example once you have the MethodInfo you can get the ReturnType Type MyType = Type.GetType("System.Reflection.FieldInfo"); MethodInfo Mymethodinfo = MyType.GetMethod("GetValue"); Console.Write ("\n" + MyType.FullName + "." + Mymethodinfo.Name); Console.Write ("\nReturnType = {0}", Mymethodinfo.ReturnType); GetParameters will tell you the parameters: Type delegateType = typeof(MainClass).GetEvent("ev").EventHandlerType; MethodInfo invoke = delegateType.GetMethod("Invoke"); ParameterInfo[] pars = invoke.GetParameters(); foreach (ParameterInfo p in pars) { Console.WriteLine(p.ParameterType); } Once you have this information you can use Invoke to actually call the method. Its first argument is the "object on which to invoke the method". Its second argument is the argument list for said method.
The code I'm providing works with the ExampleClass you provided, but in no way is it a complete solution. You need to take generics, ref, out params, and probably a whole lot of other things into consideration. public void CallAllMethods(object instance) { foreach (MethodInfo method in instance.GetType().GetMethods()) { if (method.IsGenericMethod || method.DeclaringType == typeof(object)) { // skipping, System.Object method or a generic method continue; } var defaultParamValues = method.GetParameters().Select(p => GetDefaultValue(p.ParameterType)).ToArray(); Console.WriteLine("Invoking {0} with param values {1}", method.Name, string.Join(", ", defaultParamValues)); object retVal = method.Invoke(instance, defaultParamValues); if (method.ReturnType != typeof(void)) { Console.WriteLine(" and returned a value of {0}", retVal); } } } public static object GetDefaultValue(Type type) { return type.IsValueType ? Activator.CreateInstance(type) : null; }
Checking a MethodInfo against a delegate
How can I determine if a MethodInfo fits a distinct Delegate Type? bool IsMyDelegate(MethodInfo method); Edit: I'm given a MethodInfo object and want to know if it fits the delegate interface. Apart from the obvious private bool IsValidationDelegate(MethodInfo method) { var result = false; var parameters = method.GetParameters(); if (parameters.Length == 2 && parameters[0].ParameterType == typeof(MyObject1) && parameters[1].ParameterType == typeof(MyObject2) && method.ReturnType == typeof(bool)) { result = true; } else { m_Log.Error("Validator:IsValidationDelegate", "Method [...] is not a ValidationDelegate."); } return result; }
If method is a static method: bool isMyDelegate = (Delegate.CreateDelegate(typeof(MyDelegate), method, false) != null); If method is an instance method: bool isMyDelegate = (Delegate.CreateDelegate(typeof(MyDelegate), someObj, method, false) != null) (Unfortunately you need an instance in this case because Delegate.CreateDelegate is going to try to bind a delegate instance, even though in this case all we care about it whether the delegate could be created or not.) In both cases, the trick is basically to ask .NET to create a delegate of the desired type from the MethodInfo, but to return null rather than throwing an exception if the method has the wrong signature. Then testing against null tells us whether the delegate had the right signature or not. Note that because this actually tries to create the delegate it will also handle all the delegate variance rules for you (e.g. when the method return type is compatible but not exactly the same as the delegate return type).
How do i know when a lambda expression is null
I need to programatically check whether a nested property/function result in a lambda expression is null or not. The problem is that the null could be in any of the nested subproperties. Example. Function is: public static bool HasNull<T, Y>(this T someType, Expression<Func<T, Y>> input) { //Determine if expression has a null property } Use: person.HasNull(d=>d.addressdetails.Street) person.HasNull(d=>d.addressdetails[1].Street) person.HasNull(d=>d.addressdetails.FirstOrDefault().Street) person.HasNull(d=>d.InvoiceList.FirstOrDefault().Product.Name) In any of the examples addressdetails or street, or invoicelist or the product or the name could be null.The code will throw an exception if i try to invoke the function and some nested property is null. Important: I don't want to use a try catch for this because that is desastrous for the debugging performance. The reason for this approach is to quickly check for values while i don't want to forget any nulls and so cause exceptions. This is handy for reporting solutions and grids where a null on the report can just show empty and has no futher business rules. related post: Don't stop debugger at THAT exception when it's thrown and caught
It is possible, but I'm not sure I would recommand it. Here is something that you may find usefull: it doesn't return a boolean, but instead, the leaf value of the expression if possible (no null reference). public static class Dereferencer { private static readonly MethodInfo safeDereferenceMethodInfo = typeof (Dereferencer).GetMethod("SafeDereferenceHelper", BindingFlags.NonPublic| BindingFlags.Static); private static TMember SafeDereferenceHelper<TTarget, TMember>(TTarget target, Func<TTarget, TMember> walker) { return target == null ? default(TMember) : walker(target); } public static TMember SafeDereference<TTarget, TMember>(this TTarget target, Expression<Func<TTarget, TMember>> expression) { var lambdaExpression = expression as LambdaExpression; if (lambdaExpression == null) return default(TMember); var methodCalls = new Queue<MethodCallExpression>(); VisitExpression(expression.Body, methodCalls); var callChain = methodCalls.Count == 0 ? expression.Body : CombineMethodCalls(methodCalls); var exp = Expression.Lambda(typeof (Func<TTarget, TMember>), callChain, lambdaExpression.Parameters); var safeEvaluator = (Func<TTarget, TMember>) exp.Compile(); return safeEvaluator(target); } private static Expression CombineMethodCalls(Queue<MethodCallExpression> methodCallExpressions) { var callChain = methodCallExpressions.Dequeue(); if (methodCallExpressions.Count == 0) return callChain; return Expression.Call(callChain.Method, CombineMethodCalls(methodCallExpressions), callChain.Arguments[1]); } private static MethodCallExpression GenerateSafeDereferenceCall(Type targetType, Type memberType, Expression target, Func<ParameterExpression, Expression> bodyBuilder) { var methodInfo = safeDereferenceMethodInfo.MakeGenericMethod(targetType, memberType); var lambdaType = typeof (Func<,>).MakeGenericType(targetType, memberType); var lambdaParameterName = targetType.Name.ToLower(); var lambdaParameter = Expression.Parameter(targetType, lambdaParameterName); var lambda = Expression.Lambda(lambdaType, bodyBuilder(lambdaParameter), lambdaParameter); return Expression.Call(methodInfo, target, lambda); } private static void VisitExpression(Expression expression, Queue<MethodCallExpression> methodCallsQueue) { switch (expression.NodeType) { case ExpressionType.MemberAccess: VisitMemberExpression((MemberExpression) expression, methodCallsQueue); break; case ExpressionType.Call: VisitMethodCallExpression((MethodCallExpression) expression, methodCallsQueue); break; } } private static void VisitMemberExpression(MemberExpression expression, Queue<MethodCallExpression> methodCallsQueue) { var call = GenerateSafeDereferenceCall(expression.Expression.Type, expression.Type, expression.Expression, p => Expression.PropertyOrField(p, expression.Member.Name)); methodCallsQueue.Enqueue(call); VisitExpression(expression.Expression, methodCallsQueue); } private static void VisitMethodCallExpression(MethodCallExpression expression, Queue<MethodCallExpression> methodCallsQueue) { var call = GenerateSafeDereferenceCall(expression.Object.Type, expression.Type, expression.Object, p => Expression.Call(p, expression.Method, expression.Arguments)); methodCallsQueue.Enqueue(call); VisitExpression(expression.Object, methodCallsQueue); } } You can use it this way: var street = person.SafeDereference(d=>d.addressdetails.Street); street = person.SafeDereference(d=>d.addressdetails[1].Street); street = person.SafeDereference(d=>d.addressdetails.FirstOrDefault().Street); var name = person.SafeDereference(d=>d.InvoiceList.FirstOrDefault().Product.Name); Warning : this is not fully tested, it should work with methods and properties, but probably not with extension methods inside the expression. Edit : Ok, it can't handle extension methods for now (e.g. FirstOrDefault) but it's still possible to adjust the solution.
You would have to take the expression apart and evaluate each bit in turn, stopping when you got a null result. This wouldn't be impossible by any means, but it would be quite a lot of work. Are you sure this is less work than just putting explicit null guards in the code?
We definately need a null-safe dereferencing operator in C#, but until then look at this question, which provides a slightly different but also neat solution to the same problem.
Any reason why you couldn't just do the following? bool result; result = addressdetails != null && addressdetails.Street != null; result = addressdetails != null && addressdetails.Count > 1 && addressdetails[1].Street != null; result = addressdetails != null && addressdetails.FirstOrDefault() != null && addressdetails.FirstOrDefault().Street != null; result = addressdetails != null && addressdetails.FirstOrDefault() != null && addressdetails.FirstOrDefault().Product != null && addressdetails.FirstOrDefault().Product.Name != null; I think these simple boolean expressions would be the best way to go, they work because of conditional evaluation... When anding (&&) terms together, the first false term will return false and stop the rest from being evaluated, so you wouldn't get any exceptions doing this.
Though it's not the answer on this exact question, the easiest way I know to achieve the same behaviour is to pass pathes to properties as enumerables of property names instead of call chains. public static bool HasNull<T, Y>(this T someType, IEnumerable<string> propertyNames) Then parse those enumerables in a circle or recursively using reflection. There are some disadvantages like loosing intelly sense and static name checking, but very easy to implement, which may overweigh them in some cases. Instead of writing person.HasNull(d=>d.addressdetails.FirstOrDefault().Street) you'll have to write person.HasNull(new string[] { "addressdetails", "0", "Street" })
I blogged about, it works in VB.NET (I translated to C#, but I didn't test the C# version, check it out). Checkout: How would you check car.Finish.Style.Year.Model.Vendor.Contacts.FirstOrDefault().FullName for null? :-) Dim x = person.TryGetValue( Function(p) p.addressdetail(1).FirstOrDefault().Product.Name)