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);
}
Related
I want to check if the method for certain parameters is implemented. In this scenario, I have for example these method overloads:
public class Transformations
{
public string TransformFrom(string s) { return s;}
public string TransformFrom(int i) { return "00" + i.ToString();}
public string TransformFrom(DateTime dt) { return
DateTime.ToString("MM/dd/yyyy HH:mm:ss.fff");}
}
Suppose I have decimal like:
object obj = 1M;
If I now call TransformFrom(obj), I get an exception.
Other part of my program returns unfortunately only objects, which really are concrete types like int or datetime. And I want to transform some of these objects to string so that those can be written to the log. So this is a runtime problem. Also I want that my Transformations-class is general enough to be used in other programs too, so there can be objects of any type.
Is there fast way to find out that this method overload does not exist?
Either way you are currently going to get a compile time error, even if there was an overload that accepted a decimal. You can approach this one of 2 ways depending on your needs:
To resolve this at compile time you have to cast to the correct type.
object obj = 1;
var objAsInt = (int) obj;
var result = transformationInstance.TransformFrom(objAsInt);
If there is no proper overload you will get a compile time error and you can resolve this at design time.
To resolve this at runtime use reflection to figure out if there is an overload of the type and if there is pass the instance.
object obj = 1;
var underlyingType = obj.GetType();
var method = typeof(Transformations).GetMethod("TransformFrom", new Type[] { underlyingType });
if(method != null)
{
var result = method.Invoke(transformationInstance, new []{obj});
}
It is possible to find out whether a given overload exists using reflection:
public static bool HasOverloadForArgument(Type targetType, string methodName, object arg)
{
var methodInfo = targetType.GetMethod(name: methodName, types: new[] { arg.GetType() });
return methodInfo != null;
}
Live sample
I'm not sure what is the whole idea here as there won't be much you can do when the transformation is not available during runtime.
Maybe, you'd just be best off using dynamic to avoid all the reflection stuff and caching required otherwise, e.g.
var boxedInt = (object)1;
var boxedFloat = (object)1f;
dynamic dynamicInt = boxedInt;
dynamic dynamicFloat = boxedFloat;
var intResult = new Transformations().TransformFrom(dynamicInt); // works
var floatResult = new Transformations().TransformFrom(dynamicFloat); // throws binder exception
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);
I have list of public void function that get one parameter to execute and I want to use loop to do it,
I do not know how to do this, can you advise me?
I thought to insert the names of mt functions to arr and than run in loop
something like this
string[] s1 = new string[3] {"func1", "func2", "func3"};
for(int i=0;i<s1.lengh;i++)
here I want to call the function ... how can I do it?
Do you have better offer?
Thanks.
You can pass functions as parameters / arguments by the use of delegates:
Action<T> action1 = func1;
Action<T> action2 = func2;
where T is the type of the parameter (e.g. int, string)
You can then run these referenced functions by calling
action1(t);
action2(t);
where t is the parameter for your function.
To make this example useful, consider creating a list of actions:
List<Action<T>> actions = new List<Action<T>>();
actions.Add(action1); actions.Add(action2);
foreach (Action<T> action in actions)
{
var t = param; // Replace param with the input parameter
action(t);
}
Of course, you must also have
using System;
at the top of your code file to reference Action.
See also the MSDN documentation on the Action delegate: http://msdn.microsoft.com/en-us/library/018hxwa8.aspx
Your first option is to use delegates (assuming the argument is an integer):
var s1 = new Action<int>[3] { a => func1(a), a => func2(a), a => func3(a) }; // without quotes it creates a function pointer
for(int i=0;i<s1.Length;i++)
s1[i](parameter); // call the delegate
If you do not know the function names at compile time, use reflection to call the method:
var s1 = new string[3] {"func1", "func2", "func3"};
for(int i=0;i<s1.Length;i++)
this.GetType().GetMethod(s1[i]).Invoke(this, new object[] { parameter });
Note the this.GetType() in the second sample - if the methods are defined on another type you will most probably use typeof(OtherType) instead.
Use a delegate. For example, to invoke a method that takes one parameter and returns no value (i.e. returns void), use the Action<T> delegate. Assuming that you want them all to accept the same parameter type, it would look a bit like this:
public void Action1(int x) { ... }
public void Action2(int x) { ... }
public void Action3(int x) { ... }
...
Action<int>[] actions = new Action<int>[] { Action1, Action2, Action3 }
for (int i = 0; i < actions.Length; i++)
{
actions[i](i); // Invoke the delegate with (...)
}
Further Reading
Delegates (C# Programming Guide)
I believe what you are wanting to do could be accomplished via a collection of actions.
Assuming the type of parameter for each function is integer, here's how that could look:
List<Action<int>> functions = new List<Action<int>> {func1, func2, func3};
int i = 5;
foreach (Action<int> f in functions)
{
f(i);
}
EDIT: updated per updated OP that specifies the looping should only be over each of the functions.
var list = new List<Action<MyParameterType>>() {func1, func2, func3};
foreach(var func in list)
{
func(someValue);
}
string[] s1 = new string[3] {"func1", "func2", "func3"};
for(int i=0;i<s1.lengh;i++)
List<string, Func<string>> functionList = new List<string, Func<string>>();
functionList.Add(s1[0], ()=>{return "You called func1!";});
functionList.Add(s1[1], ()=>{return "You called func2!";});
functionList.Add(s1[2], ()=>{return "You called func3!";});
for(int i=0;i<s1.length;i++)
{
string retVal = functionList[s1[i]].Invoke();
}
We're currently using .NET 3.5 and part of our application uses dynamic invocation (using MethodBase.Invoke)
I am wondering if it is possible to mix in Named Parameters (in .NET 4) with dynamic invocation, to perform something similar to:
// Dictionary that holds parameter name --> object mapping
var parameters = new Dictionary<string, object>();
// Add parameters ....
// Invoke where each parameter will match the one from the method signature.
methodInfo.Invoke(obj, parameters);
Is there any API that allows this option out of the box? If not, is it possible to develop some solution to perform this?
EDIT:
Rethinking of this problem, it sounds similar to how the compiler may actually need to match method calls based on argument lists. Perhaps there's some Compiler API (or the new Roslyn project) that allows doing just this easily? (without coding it myself which may be prone to errors).
You can use code like this:
public static class ReflectionExtensions {
public static object InvokeWithNamedParameters(this MethodBase self, object obj, IDictionary<string, object> namedParameters) {
return self.Invoke(obj, MapParameters(self, namedParameters));
}
public static object[] MapParameters(MethodBase method, IDictionary<string, object> namedParameters)
{
string[] paramNames = method.GetParameters().Select(p => p.Name).ToArray();
object[] parameters = new object[paramNames.Length];
for (int i = 0; i < parameters.Length; ++i)
{
parameters[i] = Type.Missing;
}
foreach (var item in namedParameters)
{
var paramName = item.Key;
var paramIndex = Array.IndexOf(paramNames, paramName);
if (paramIndex >= 0)
{
parameters[paramIndex] = item.Value;
}
}
return parameters;
}
}
And then call it like this:
var parameters = new Dictionary<string, object>();
// Add parameters ...
methodInfo.InvokeWithNamedParameters(obj, parameters);
you can get your paramter names with the help of this article How can you get the names of method parameters? and then you can reorder them to invoke them as described here Reflection: How to Invoke Method with parameters
With .net4, I have an opensource framework ImpromptuInterface (found in nuget) that makes it easy to use the DLR apis for late invocation including named/optional parameters.
var result = Impromptu.InvokeMember(target, "MyMethod", parameters.Select(pair=> InvokeArg.Create(pair.Key, pair.Value)).Cast<object>().ToArray());
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'