MethodInfo.Invoke parameter order - c#

I'm trying to invoke a method using reflection.
Something like this:
method.Invoke(instance, propValues.ToArray())
The problem is that there isn't a way to ensure the array of parameters is in the right order. Is there a way to specific which values goes on which parameter by name? Or do I really have to make a custom binder? If so, can anyone guide me in the right direction?

Is there a way to specific which values goes on which parameter by name?
Well, you specify them in parameter order. So if you want to map specific values to specific names, you should fetch the parameter list with method.GetParameters and map them that way. For example, if you had a Dictionary<string, object> with the parameters:
var arguments = method.GetParameters()
.Select(p => dictionary[p.Name])
.ToArray();
method.Invoke(instance, arguments);

EDIT: This answer focuses on parameter types not the parameter names. If the code is obfuscated (or having different param names) then it will be difficult to map the solution that Jon Skeet has provided.
Anyway, I had been playing with this a lot.... This is what works best for me (without knowing param names) :
public object CallMethod(string method, params object[] args)
{
object result = null;
// lines below answers your question, you must determine the types of
// your parameters so that the exact method is invoked. That is a must!
Type[] types = new Type[args.Length];
for (int i = 0; i < types.Length; i++)
{
if (args[i] != null)
types[i] = args[i].GetType();
}
MethodInfo _method = this.GetType().GetMethod(method, types);
if (_method != null)
{
try
{
_method.Invoke(this, args);
}
catch (Exception ex)
{
// instead of throwing exception, you can do some work to return your special return value
throw ex;
}
}
return result;
}
so, you can call the above function:
object o = CallMethod("MyMethodName", 10, "hello", 'a');
The above call will should be able to invoke this method with matching signature:
public int MyMethodName(int a, string b, char c) {
return 1000;
}
Please note that he above example is in the scope of 'this'

Related

Method.Invoke failing with Parameter Count Mismatch

I am trying to invoke a generic method. The definition of the method is as follows:
public System.Collections.Generic.IList<T> Query<T>(string query, [string altUrl = ""])
where T : new()
This is from the SalesforceSharp library on github. I am trying to make an additional service layer over this call and am struggling to invoke it. See my code below.
public List<T> Query<T>()
{
//IList<Salesforce.Account> _returnList = null;
IList<T> _returnList = null;
Type _t = typeof(T);
SqlBuilder _sb = new SqlBuilder();
_sb.Table = _t.Name.ToString();
foreach (PropertyInfo p in _t.GetProperties()) _sb.Fields.Add(p.Name.ToString());
MethodInfo method = _Client.GetType().GetMethod("Query");
method = method.MakeGenericMethod(_t);
try
{
object[] _prms = new object[1];
_prms[0] = _sb.SQL;
_returnList = (IList<T>)method.Invoke(_Client, new object[] { _prms });
//_returnList = _Client.Query<Salesforce.Account>(_sb.SQL);
}
catch { }
return (List<T>)_returnList;
}
If I run this i get a Parameter Count Mismatch exception on the method.invoke line, but i am confused because if i bring in the two uncommented lines and execute without the generic call it is working ok. I have tried many combinations of string arrays wrapped in object arrays, strings in strings, etc but can't get it to go. I thought maybe it was treating the second parameter as mandatory? but adding another object to my _prms array didnt work either.
Please help!
Thanks,
Dom
For optional parameters that you don't want to specify a value for, you have to pass Type.Missing like this:
_returnList = (IList<T>)method.Invoke(_Client, new object[] { _sb.SQL, Type.Missing });
Quoting from this reference:
Use the Missing field for invocation through reflection to obtain the default value of a parameter
Please note also that another problem is that you are currently passing an object[] instead of just the query string.
The optional parameters are just syntactic sugars, when you don't supply an optional parameter compiler calls the method with the given default value. But in case of Reflection, you need to do it manually.
object[] _prms = new object[2];
_prms[0] = _sb.SQL;
_prms[1] = "";
_returnList = (IList<T>)method.Invoke(_Client, _prms);

Call dynamic method from string

I'm trying to call a method from a dynamic without knowing its name. I have difficulties to explain this in english so there's the code:
public void CallMethod(dynamic d, string n)
{
// Here I want to call the method named n in the dynamic d
}
I want something like:d.n() but with n replaced by the string.
I want this :
Type thisType = this.GetType();
MethodInfo theMethod = thisType.GetMethod(TheCommandString);
theMethod.Invoke(this, userParameters);
but with dynamic.
If you need the context to help you: I'm make an application that's support "mods", you put DLLs in the mod folder and it loads it and execute it. It works with dynamic (I have a dictionnary like this : Dictionnary<string, dynamic> instances;). I want the application to get the methods name from the library (with instances["topkek"].GetMethods();, I've already made this method) but then call the method with the string it returns. I don't know if what I said mean something (I'm french :/ )...
I'm using VS 2013 Express with the .Net framework 4.5, if you need more information to help me ask me.
you can write your method as follows -
public void CallMethod(dynamic d, string n)
{
d.GetType().GetMethod(n).Invoke(d, null);
}
If all methods are void, this could work. Otherwise you need to change it a bit.
public void CallMethod(string className, string methodName)
{
object dynamicObject;
// Here I want to call the method named n in the dynamic d
string objectClass = "yourNamespace.yourFolder." + className;
Type objectType = Type.GetType(objectClass);
if (objectType == null)
{
// Handle here unknown dynamic objects
}
else
{
// Call here the desired method
dynamicObject = Activator.CreateInstance(objectType);
System.Reflection.MethodInfo method = objectType.GetMethod(methodName);
if (method == null)
{
// Handle here unknown method for the known dynamic object
}
else
{
object[] parameters = new object[] { }; // No parameters
method.Invoke(dynamicObject, parameters);
}
}
}
I want to add another approach as solution:
In your case, the caller (developer of the mod) knows the method to call. Thus, this might helpful:
// In the main application:
public dynamic PerformMethodCall(dynamic obj, Func<dynamic, dynamic> method)
{
return method(obj);
{
// In a mod:
mainProgram.PerformMethodCall(myDynamicObj, n => n.myDynamicMethod());
// In another mod:
mainProgram.PerformMethodCall(myDynamicObj, n => n.anotherMethod());
This is a further development of the idea of Yuval Itzchakov in his commentary. He had suggested using a delegate.

Passing params object[] through jQuery to C# method

I am trying to pass a params object[] through a jQuery to a C# method. I'm using this to use the same method through jQuery calls, sending a string that would be the real method to call and the params object[] that is the parameters to this call, obviously the number of parameters is unknown since I don't know exactly what method would be call, here is the code on jQuery:
$('#selectComboBox').change(function () {
var data = {
'method': 'GetComboBoxValues',
'arguments': $.param({ Id: this.value })
};
LoadComboBox('/Get/GetJsonResult', data, $('#destionationComboBox'))
})
The LoadComboBox function is a simple function that I centered to populate comboboxes:
function LoadComboBox(url, data, select) {
select.empty();
$.getJSON(url, data, function (a) {
$(a).each(function () {
$(document.createElement('option')).prop('value',this.Value).text(this.Text).appendTo(select);
});
});
}
My C# code is below:
public string GetJsonResult(string method, params object[] arguments)
{
var methodInfo = this.GetType().GetMethod(method);
var l = methodInfo.Invoke(this, arguments);
return new JavaScriptSerializer().Serialize(l);
}
I get arguments as a object array and it is filled with a string Id=1 (with $('#selectComboBox').value being 1). I was not able to perform a Split('=') in a new array because if the real method (GetComboBoxValues) is not expecting a string (in this case is a INT) it would not be dynamically converted.
Do anyone has any tips or clues?
This was a really interesting question. It seems like your main issue is dynamically converting from an object array to a bunch of required parameter types of a dynamically selected method. In short, this can be done using methodInfo.GetParameters(); and using Convert.ChangeType to convert each of your arguments into the appropriate ParameterType. This is probably best seen in action, so I made a small Forms app that does this. Of course, this all makes a ton of assumptions that what is passed in will be "clean" so a lot of error handling is probably in order.
private void button1_Click(object sender, EventArgs e)
{
//mock up some dynamically passed in parameters
var testParams = new List<object>();
testParams.Add("1");
testParams.Add("Hello");
//the args I'm building up to pass to my dynamically chosen method
var myArgs = new List<object>();
//reflection to get the method
var methodInfo = this.GetType().GetMethod("test");
var methodParams = methodInfo.GetParameters();
//loop through teh dynamic parameters, change them to the type of the method parameters, add them to myArgs
var i = 0;
foreach (var p in methodParams)
{
myArgs.Add(Convert.ChangeType(testParams[i], p.ParameterType));
i++;
}
//invoke method
var ans = methodInfo.Invoke(this, myArgs.ToArray());
//display answer
MessageBox.Show((string)ans);
}
public string test(int i, string s)
{
return s + i.ToString();
}
As an aside, in my opinion, this leads to some crazy code that's tough to maintain (you're trying to do things with C# that it wasn't really meant to do). But you didn't really ask anyone's opinion, so I'll leave that as an aside.
Mike Bell lead me to the answer, his idea just needed some adjusments, commented below, the answer was on editing the GetJsonResult method, to this:
public string GetJsonResult(string method, params object[] arguments)
{
var methodInfo = this.GetType().GetMethod(method);
var methodParameters = methodInfo.GetParameters();
var parameters = new List<object>();
for (int i = 0; i < methodParameters.Length; i++)
{
// Here I'm getting the parameter name and value that was sent
// on the arguments array, we need to assume that every
// argument will come as 'parameterName=parameterValue'
var pName = arguments[i].ToString().Split('=')[0];
var pValue = arguments[i].ToString().Split('=')[1];
// This way I can get the exact type for the argument name that I'm sending.
var pInfo = methodParameters.First(x => x.Name == pName);
parameters.Add(Convert.ChangeType(pValue,
// This is needed because we may be sending a parameter that is Nullable.
Nullable.GetUnderlyingType(pInfo.ParameterType) ?? pInfo.ParameterType));
}
var l = methodInfo.Invoke(this, parameters.ToArray());
return new JavaScriptSerializer().Serialize(l);
}

Instantiate object via reflection with anonymous type

I'm using the following method to instantiate an object via reflection
Activator.CreateInstance(Type type, params object[] parameters)
Where "parameters" is the list of parameters passed to the constructor at runtime.
However, I'd like this process to be more intuitive to other developers on the team and rather than passing object[] array of parameters, I would like them to pass an anonymous object, e.g.
// note, invalid code
Activator.CreateInstance(typeof(MyType), new { paramName1 = "abc", paramName2 = "xyz})
Since the framework method doesn't support it, does anyone have an example of the code that translates an anonymous object into an array? Note that the order of the parameters is important to the Activator.CreateInstance() method since that's how it does parameter matching. Obviously this is error prone, that's why I'd prefer to use an anonymous type.
Any suggestions are gladly appreciated.
Alec.
I wouldn't use Activator.CreateInstance for this. I'd use Type.GetConstructors() to get all the constructors, and then find one which has the same number of parameters as the anonymous type has properties, and with the same names. If there might be multiple such constructors with different types, you'll need to add extra logic to check that each parameter type is compatible with the relevant property type.
Something like:
public object CreateInstance(Type type, Object parameterMapping)
{
var ctors = type.GetConstructors();
var properties = parameterMapping.GetType().GetProperties();
foreach (var ctor in ctors)
{
var parameters = ctor.GetParameters();
if (parameters.Length != properties.Length)
{
continue;
}
object[] args = new object[parameters.Length];
bool success = true;
for (int i = 0; i < parameters.Length;
{
var property = parameterMapping.GetType().GetProperty(parameter.Name);
if (property == null)
{
success = false;
break;
}
// TODO: Check property type is appropriate too
args[i] = property.GetValue(parameterMapping, null);
}
if (!success)
{
continue;
}
return ctor.Invoke(args);
}
throw new ArgumentException("No suitable constructor found");
}

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

Categories

Resources