Dynamic invoke of a method using named parameters - c#

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

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.

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

MethodInfo.Invoke parameter order

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'

How to dynamcially call a generic method based on a mapping in a dictionary?

I have a method
String Foo<T> where T: WebControl
Now I do have a string like "hyperlink". What is want is to call Foo<Hyperlink> based on a mapping from the string to the generic.
How does the dictionary have to look like?
It ain't:
private Dictionary<string, Type> _mapping = new Dictionary<string, Type>()
{
{"hyperlink", typeof(HyperLink)}
};
I want to access it like Foo<_mapping[mystring]> is that possible? If yes, how must be dictionary look like?
Edit: Accepted Solution
String _typename = "hyperlink";
MethodInfo _mi = typeof(ParserBase).GetMethod("Foo");
Type _type = _mapping[_typename];
MethodInfo _mig = _mi.MakeGenericMethod(_type);
return (String)_mig.Invoke(this, new object[] { _props }); // where _props is a dictionary defined elsewhere
// note that the first parameter for invoke is "this", due to my method Foo is not static
What you want isn't possible, as that would be runtime (e.g. the dictionary could contain anything later).
If you want to manually generate it via runtime, you can do so, but you won't get the compile-time checking C# has on generics. You can to this via MethodInfo.MakeGenericMethod.
Like this:
var m = typeof(MyClass);
var mi = ex.GetMethod("Foo");
var mig = mi.MakeGenericMethod(_mapping["hyperlink"]);
//Invoke it
mig .Invoke(null, args);
It isn't possible this way. Generics supports only compile-tipe binding.
No, you can't do that. Your generic type wants to create itself at compile time but it doesn't know what type it is til runtime. You can, however, use reflection.
Type untypedGeneric = typeof(Foo<>);
Type typedGeneric = untypedGeneric.MakeGenericType(_mapping[mystring]);

Categories

Resources