Handling null parameters when calling a method using Reflection

I am trying to write code that will infer types from a list of parameters, and then call a method that matches these parameters. This works very well, unless the parameter list is null in it.

I am wondering how I can call a Type.GetMethod call to match the function / overload, even with the null parameter in the parameter list.

 object CallMethodReflection(object o, string nameMethod, params object[] args) { try { var types = TypesFromObjects(args); var theMethod = o.GetType().GetMethod(nameMethod, types); return (theMethod == null) ? null : theMethod.Invoke(o, args); } catch (Exception ex) { return null; } } Type[] TypesFromObjects(params object[] pParams) { var types = new List<Type>(); foreach (var param in pParams) { types.Add((param == null) ? null : param.GetType()); } return types.ToArray(); } 

The main problem is types.Add((param == null) ? null : param.GetType()); , which will cause the GetMethod call to fail using the null value in the type array.

 void Function1(string arg1){ } void Function1(string arg1, string arg2){ } void Function1(string arg1, string arg2, string arg3){ } void Function2(string arg1){ } void Function2(string arg1, int arg2){ } void Function2(string arg1, string arg2){ } /*1*/ CallMethodReflection(obj, "Function1", "String", "String"); // This works /*2*/ CallMethodReflection(obj, "Function1", "String", null); // This doesn't work, but still only matches one overload /*3*/ CallMethodReflection(obj, "Function2", "String", "String"); // This works /*4*/ CallMethodReflection(obj, "Function2", "String", null); // This doesn't work, and I can see why this would cause problems 

Basically, I am trying to determine how to change my code so that the line /*2*/ works.

+8
reflection null c #
source share
8 answers

There are getMethod call overrides that take an object obtained from the Binder class. This allows you to override the default method binding and return the method you want to use based on the actual parameters passed. These are essentially two other answers. Here is a sample code:

http://msdn.microsoft.com/en-us/library/system.reflection.binder.aspx

+6
source share

An option that was not mentioned is the use of Fasterflect , a library designed to facilitate and accelerate reflection tasks (using IL generation).

To call a method defined in a dictionary with named parameters (or an object with properties that should be used as parameters), you can call the best match as follows:

 obj.TryCallMethod( "SomeMethod", argsDictionary ); obj.TryCallMethod( "AnotherMethod", new { Foo = "Bar" } ); 

If you have parameter values ​​and their order, you can use another overload:

 obj.TryCallMethodWithValues( "MyMethod", 42, "foo", "bar", null, 2.0 ); 

PS: you will need to get the last bits from the original control in order to use the TryCallMethodWithValues ​​extension.

Disclaimer: I participate in the Fasterflect project.

+5
source share

For any parameter that is null, you can simply match any reference type. The following very simple / naive code will work for your methods, as shown in the figure, but it does not handle things such as ambiguity exceptions or more complex cases using ref / out parameters or the ability to pass a derived type to a method or general methods.

If you are using 4.0, then just using the dynamic option may be the best choice.

 object CallMethodReflection(object o, string nameMethod, params object[] args) { try { var types = TypesFromObjects(args); var oType = o.GetType(); MethodInfo theMethod = null; // If any types are null have to perform custom resolution logic if (types.Any(type => type == null)) { foreach (var method in oType.GetMethods().Where(method => method.Name == nameMethod)) { var parameters = method.GetParameters(); if (parameters.Length != types.Length) continue; //check to see if all the parameters match close enough to use bool methodMatches = true; for (int paramIndex = 0; paramIndex < parameters.Length; paramIndex++) { //if arg is null, then match on any non value type if (args[paramIndex] == null) { if (parameters[paramIndex].ParameterType.IsValueType) { methodMatches = false; break; } } else //otherwise match on exact type, !!! this wont handle things passing a type derived from the parameter type !!! { if (parameters[paramIndex].ParameterType != args[paramIndex].GetType()) { methodMatches = false; break; } } } if (methodMatches) { theMethod = method; break; } } } else { theMethod = oType.GetMethod(nameMethod, types); } Console.WriteLine("Calling {0}", theMethod); return theMethod.Invoke(o, args); } catch (Exception ex) { Console.WriteLine("Could not call method: {0}, error: {1}", nameMethod, ex.ToString()); return null; } } 
+3
source share

I think you have to do:

 var methods = o.GetType().GetMethods().Where(m => m.Name == methodName); 

Then essentially do your own overload resolution. First you can try your existing method, catch the exception, and then try the above.

+2
source share

Thanks to the MSDN link , as well as some additional discussion of SO and a discussion of an external forum with the participation of a well-known member of SO , I tried to implement my own solution, which still works for me.

I created a class that inherited the Binder class and put my logic to handle potentially null arguments / types there.

 object CallMethodReflection(object o, string nameMethod, params object[] args) { try { var types = TypesFromObjects(args); var theMethod = o.GetType().GetMethod(nameMethod, CustomBinder.Flags, new CustomBinder(), types, null); return (theMethod == null) ? null : theMethod.Invoke(o, args); } catch (Exception ex) { return null; } } Type[] TypesFromObjects(params object[] pParams) { var types = new List<Type>(); foreach (var param in pParams) { types.Add((param == null) ? typeof(void) : param.GetType()); // GetMethod above doesn't like a simply null value for the type } return types.ToArray(); } private class CustomBinder : Binder { public const BindingFlags Flags = BindingFlags.Public | BindingFlags.Instance; public override MethodBase SelectMethod(BindingFlags bindingAttr, MethodBase[] matches, Type[] types, ParameterModifier[] modifiers) { if (matches == null) throw new ArgumentNullException("matches"); foreach (var match in matches) { if (MethodMatches(match.GetParameters(), types, modifiers)) return match; } return Type.DefaultBinder.SelectMethod(bindingAttr, matches, types, modifiers); // No matches. Fall back to default } private static bool MethodMatches(ParameterInfo[] parameters, Type[] types, ParameterModifier[] modifiers) { if (types.Length != parameters.Length) return false; for (int i = types.Length - 1; i >= 0; i--) { if ((types[i] == null) || (types[i] == typeof(void))) { if (parameters[i].ParameterType.IsValueType) return false; // We don't want to chance it with a wonky value } else if (!parameters[i].ParameterType.IsAssignableFrom(types[i])) { return false; // If any parameter doesn't match, then the method doesn't match } } return true; } } 

Since the Binder class is an abstract class, you must override several other members in order to actually use this code, but most of my overrides are only before the Type.DefaultBinder object.

 public override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] matches, object value, CultureInfo culture) { return Type.DefaultBinder.BindToField(bindingAttr, matches, value, culture); } 
+1
source share

I have not tested it, and I think the other answers are much better, but I wonder why this will not work:

 foreach (var param in pParams.Where(p => p != null) { types.Add(param.GetType()); } 
0
source share

You can approach the problem by implementing your own GetMethod method, which iterates through the whole method in the object and determines which one is best suited, I hope this helps. I checked the following method with your example and worked

 MethodInfo SmarterGetMethod(object o, string nameMethod, params object[] args) { var methods = o.GetType().GetMethods(); var min = args.Length; var values = new int[methods.Length]; values.Initialize(); //Iterates through all methods in o for (var i = 0; i < methods.Length; i += 1) { if (methods[i].Name == nameMethod) { var parameters = methods[i].GetParameters(); if (parameters.Length == min) { //Iterates through parameters for (var j = 0; j < min; j += 1) { if (args[j] == null) { if (parameters[j].ParameterType.IsValueType) { values[i] = 0; break; } else { values[i] += 1; } } else { if (parameters[j].ParameterType != args[j].GetType()) { values[i] = 0; break; } else { values[i] += 2; } } } if (values[i] == min * 2) //Exact match return methods[i]; } } } var best = values.Max(); if (best < min) //There is no match return null; //Iterates through value until it finds first best match for (var i = 0; i < values.Length; i += 1) { if (values[i] == best) return methods[i]; } return null; //Should never happen } 
0
source share
  • If none of the parameters is NULL, you make a normal method call if it is null, but
  • else, if at least one of them is null, you take a different approach:
  • build a list of parameter types from parameters: for example, "int, char, null, int"
  • get overload functions with the same number of parameters for your function name
  • See if there is only one corresponding function, because if there are 2, you cannot determine which call (the hardest part, but rather straightforward, I think).
  • call the function you defined with your parameters and zeros
0
source share

All Articles