Calling a function using reflection that has a "params" parameter (MethodBase) - c#

I have MethodBases for two functions:
public static int Add(params int[] parameters) { /* ... */ }
public static int Add(int a, int b) { /* ... */ }
I have a function that calls the MethodBases via a class I made:
MethodBase Method;
object Target;
public object call(params object[] input)
{
return Method.Invoke(Target, input);
}
Now if I AddTwoMethod.call(5, 4); it works fine.
If I however use AddMethod.call(5, 4); it returns:
Unhandled Exception: System.Reflection.TargetParameterCountException: parameters do not match signature
Is there any way to make it so that both calls work fine without need for manually putting the arguments in an array for the params int[]?

You could modify your call method to detect the params parameter and convert the rest of the input to a new array. That way your method could act pretty much the same as the logic C# applies to the method calling.
Something i quicly constructed for you (be aware that i tested this method in a pretty limited way, so there might be errors still):
public object call(params object[] input)
{
ParameterInfo[] parameters = Method.GetParameters();
bool hasParams = false;
if (parameters.Length > 0)
hasParams = parameters[parameters.Length - 1].GetCustomAttributes(typeof(ParamArrayAttribute), false).Length > 0;
if (hasParams)
{
int lastParamPosition = parameters.Length - 1;
object[] realParams = new object[parameters.Length];
for (int i = 0; i < lastParamPosition; i++)
realParams[i] = input[i];
Type paramsType = parameters[lastParamPosition].ParameterType.GetElementType();
Array extra = Array.CreateInstance(paramsType, input.Length - lastParamPosition);
for (int i = 0; i < extra.Length; i++)
extra.SetValue(input[i + lastParamPosition], i);
realParams[lastParamPosition] = extra;
input = realParams;
}
return Method.Invoke(Target, input);
}
Be aware that i tested this method in a pretty limited way, so there might be errors still.

Supposing we have the following example class:
public class Test
{
public static int Add(int i1, int i2)
{
return i1 + i2;
}
public static int Add(params int[] ints)
{
int sum = 0;
foreach (int i in ints)
sum += i;
return sum;
}
}
To get the MethodInfo objects for each overload of the static Add method you should do the following:
MethodInfo Add2Ints = typeof(Test).GetMethod("Add", new Type[] { typeof(int), typeof(int) });
MethodInfo AddParamsInts = typeof(Test).GetMethod("Add", new Type[] { typeof(int[]) });
In order to invoke any of the two methods, symply pass the arguments with the exact type expected by the specific overload you are invoking:
Add2Ints.Invoke(null, new object[] { 1, 2 });
AddParamsInts.Invoke(null, new object[] { new int[] { 1, 2 } });
Note that the following will not work:
AddParamsInts.Invoke(null, new object[] { 1, 2 });
because the signature of AddParmsInt is really (int[]) and although the compiler, as a courtesy, allows you to call such method as (int, int) under the hood what is really happening is that the call is converted for you at the call site to the equivalent (int[]) call. Via reflection you don't have the compiler's "help" so you need to pass the exact argument type defined by the method's signature.
With all that said, your call method should be as follows:
public object call(params object[] input)
{
return AddParamsInts.Invoke(null /*static*/, new object[] { input.Cast<int>().ToArray() });
}
Note that you can not directly cast a object[] array to a int[] array: int[] ints = (int[])input. Casting reference typed arrays to value-type arrays is not allowed.
Also important to note is that the defined overloads of the Add method are useless, as they overlap. Consider only using the params overload or, in case you want to guarantee that at least two arguments are needed in order to evaluate an addition, overload them the following way:
public int Add(int i1, int i2) { }
public int Add(int i1, int i2, params int[] args) { }

You should wrap the arguments in an array, but the compiler will be confused, so you need to help it a bit:
Eg:
AddMethod.call((object) new int[] {5, 4 });

Related

C# expand array to be arguments

void foo(int one, int two) {
}
public static void Main(string[] args) {
var bar = new int[] { 1, 2 };
foo(params bar);
}
What's the correct syntax to deconstruct the bar array and pass it as the arguments to the foo method?
In some other languages you can use a splat operator foo(...bar) or an unpack operator foo(*bar).
How can I do it in C#?
There isn't an equivalent function in C#. Each argument has to be passed individually.
There are, of course, work arounds that you likely already know. You could declare an overload for your function that would accept an array and call the original function using the first two inputs. The other alternative that I can think of is to declare the function parameter with the params keyword so that it could receive an array or multiple conma-separated elements when called.
void foo(params int[] numbers)
{ // TODO: Validate numbers length
int one = numbers[0];
int two = numbers[1];
}
public static void Main(string[] args) {
var bar = new int[] { 1, 2 };
// both valid function calls below
foo(bar);
foo(bar[0], bar[1]);
}
You can always use Reflection for such purpose.
Here is example snippet on your example method:
class MainClass
{
void foo(int one, int two)
{
Console.WriteLine(one + two);
}
static void Main()
{
var myInstance = new MainClass();
var bar = new object[] { 1, 2 };
var method = myInstance.GetType().GetMethod(nameof(MainClass.foo), BindingFlags.NonPublic | BindingFlags.Instance)
?? throw new InvalidOperationException($"Method '{nameof(MainClass.foo)}' not found");
method.Invoke(myInstance, bar) ;
}
}

C# - pass on expanded params array to another function

How can I expand a params array when passing it on to another function.
Consider the following example:
public class TestClass
{
public static void Main()
{
TestParamFunc(1, "1", 1.0f, 1.0);
Console.ReadKey();
}
private static void TestParamFunc(params object[] parameters)
{
//At the moment the DeeperTestParamFunc receives the following parameters: { int, object[] }
//How to expand this to { int, int, string, float, double } ?
//So that { 1, 1, "1", 1.0f, 1.0 } is received.
DeeperTestParamFunc(1, parameters);
}
public static void DeeperTestParamFunc(params object[] parameters)
{
foreach(object obj in parameters)
{
Console.WriteLine(obj.GetType().ToString());
}
}
}
Output:
System.Int32
System.Object[]
The output that I would like to have:
System.Int32
System.Int32
System.String
System.Single
System.Double
You make a new array, copy the existing parameters into it, add more things and call the other method with the larger array
These are, when all is said and done, just arrays. params just means the compiler lets you specify arguments individually and it will turn it into an array for you
You write this:
MyFuncWithParamsObjectArg(1,2,3,4,5);
The compiler conceptually changes it to:
MyFuncWithParamsObjectArg(new object [] {1,2,3,4,5} );
Here's a demo program:
public static void Main()
{
A("z", "y");
}
static void A(params object[] a){
Console.WriteLine("In A()");
foreach(object o in a)
Console.WriteLine(o);
object[] oo = new object[a.Length + 2];
for(int i = 0; i < a.Length; i++)
oo[i] = a[i];
oo[a.Length] = "new";
oo[a.Length + 1] = "new2";
B(oo);
}
static void B(params object[] b){
Console.WriteLine("In B()");
foreach(object o in b)
Console.WriteLine(o);
}
Prints out this:
In A()
z
y
In B()
z
y
new
new2
You can see that B was called with an array 2 longer than a, and added two more elements
Note, you can't "expand" an array, regardless of whether it's params or not. You have to make a new array, copy the stuff over, and go from there
If you're looking to wrap it up so that you're not flattening the first array, but instead making another params array that has the first array as one of its elements:
static void A(params object[] a){
Console.WriteLine("In A()");
foreach(object o in a)
Console.WriteLine(o);
B(a, "new1", "new2");
}
B will now get an array that looks like:
object[] {
object[] { "z", "y" },
"new1",
"new2"
}
As noted; there is no magic - the compiler looks at what you provided and if the arguments match "called with a single dimension array" then it calls the function with the array, otherwise it scoops up the various arguments, turns them into an array, and calls it with that array
B(a); //a is already an object array, it is passed in verbatim
B(a, "new1", "new2"); //a new object array is created from these 3 objects
B(new object[] {a, "new1", "new2"} ); //manually doing what the compiler does for you above
this is what you want:
private static void TestParamFunc(params object[] parameters)
{
// manually create the array in the form you need
var deeperParameters = new object[parameters.Length + 1];
deeperParameters[0] = 1;
parameters.CopyTo(deeperParameters, 1);
DeeperTestParamFunc(deeperParameters);
}
try :
public static void DeeperTestParamFunc(params object[] parameters)
{
foreach (object obj in (IEnumerable) parameters[1])
{
Console.WriteLine(obj.GetType().ToString());
}
}
instance :
public static void DeeperTestParamFunc(params object[] parameters)
{
foreach(object obj in parameters)
{
Console.WriteLine(obj.GetType().ToString());
}
}

Passing delegate parameter to MethodInfo.Invoke

I have a window with multiple radio buttons : first group Sorting Algorithms and second directions (Ascending, Descending).
Every Sorting Method that I have contains :
public delegate bool ComparatorDelegate(int a, int b);
public static int[] sort(int[] array, ComparatorDelegate comparator){...}
and I mention that I need to keep this signatures (specially passing a delegate as a parameter).
Now the problem is , I have two methods
First one retrieves the selected algorithm
private Type getSelectedSortingMethod()
{
if (radioButtonBubbleSort.Checked)
{
return typeof(BubbleSort);
}
else if (radioButtonHeapSort.Checked)
{
return typeof(HeapSort);
}
else if (radioButtonQuickSort.Checked)
{
return typeof(QuickSort);
}
else
{
return typeof(SelectionSort);
}
}
and the second one retrieves the direction :
private Func<int, int, bool> getSelectedDirection()
{
Func<int, int, bool> selectedDirectionComparator = null;
if (radioButtonAscending.Checked)
{
selectedDirectionComparator = ComparatorUtil.Ascending;
}
else if (radioButtonDescending.Checked)
{
selectedDirectionComparator = ComparatorUtil.Descending;
}
return selectedDirectionComparator;
}
Question : How can I invoke the sort method with a delegate parameter , because passing Func throws exception ?
Exception :
Object of type 'System.Func`3[System.Int32,System.Int32,System.Boolean]' cannot be converted to type 'Lab2.SortingMethods.HeapSort+ComparatorDelegate'.
something like this :
Type sortingMethodClass = getSelectedSortingMethod();
MethodInfo sortMethod = sortingMethodClass.GetMethod("sort");
Func<int, int, bool> selectedDirectionComparator = getSelectedDirection();
int[] numbersToSort = getValidNumbers();
Object[] parameters = new Object[] {numbersToSort,selectedDirectionComparator};
sortMethod.Invoke(null, parameters);
displayNumbers(numbersToSort);
Try
Object[] parameters = new Object[]
{ numbersToSort, new ComparatorDelegate (selectedDirectionComparator)};

How can i make params `out` in C#?

I find myself in the situation requiring this
public static void Fill(this SomeClass c, params out object[] p)
and calling it as
c.Fill(out i, out i2, out sz, out i3, out sz2);
However i get the error error CS1611: The params parameter cannot be declared as ref or out
How can i pass in variable length arguments and make them writeable? All of these are a mixture of ints and strings
You can't have it treat the arguments as out (or ref) and make use of the params feature at the same time. It simply doesn't work. The best you can do is to create an array parameter, make the array out, declare an array variable and call the method passing the array, then inspect each element manually by index.
Foo(out object[] data) {...}
object[] result;
Foo(out result);
// look at result[0], result[1], result[2] etc
So: you cannot do what you want. Even if you could, ref / out never work unless there is an exact match between data type, so it would still have to be:
object o1, o2, o3, o4;
Foo(out o1, out o2, out o3, out o4);
// cast o1, o2, o3, o4
Which still isn't what you want.
There is no technical need for out here. This works:
void Fill(object[] p)
{
p[0] = 1;
p[1] = 42;
p[2] = "Hello";
p[3] = -1;
p[4] = "World";
}
object[] p = new object[5];
foo.Fill(p);
i = (int)p[0];
i2 = (int)p[1];
sz = (string)p[2];
i3 = (int)p[3];
sz2 = (string)p[4];
You could return your values as Tuple:
(define your own tuple class if you're not using .NET4.0)
static Tuple<int, string> Fill()
{
return new Tuple(42, "Hello World");
}
and then define extension methods to unpack tuples:
public static class TupleExtensions
{
public static void Unpack<T1, T2>(
this Tuple<T1, T2> tuple,
out T1 item1,
out T2 item2)
{
item1 = tuple.Item1;
item2 = tuple.Item2;
}
}
Then you can write this:
int i;
string sz;
foo.Fill().Unpack(out i, out sz);
1) If you can avoid the need to get the values in declared variables, then passing the array and populating it is the best option, as shown by dtb's answer.
2) Otherwise you can have a simple wrapper for your variable.
public class Wrapper //or may be generic?
{
public object Value { get; set; }
public Wrapper(object value)
{
Value = value;
}
}
Now you can call
var i = new Wrapper(0), i2 = new Wrapper(0), i3 = new Wrapper(0);
c.Fill(i, i2, i3);
i.Value //your value here
public static void Fill(this SomeClass c, params Wrapper[] p)
{
for (int i = 0; i < p.Length; i++)
{
p[i].Value = 1; //assigning
}
}
You will have to deal with Value property after calling Fill method.
3) You can make use of closure. Something like the Ref<T> class implemented as shown:
public static class Ref
{
public static Ref<T>[] Create<T>(params Expression<Func<T>>[] getters)
{
return getters.Select(Create).ToArray();
}
public static Ref<T> Create<T>(Expression<Func<T>> getter)
{
return new Ref<T>(getter);
}
}
public sealed class Ref<T>
{
readonly Func<T> getter;
readonly Action<T> setter;
public Ref(Expression<Func<T>> getter)
{
var output = getter.Body;
var input = Expression.Parameter(output.Type); //or hardcode typeof(T)
var assign = Expression.Assign(output, input);
var setter = Expression.Lambda<Action<T>>(assign, input);
this.getter = getter.Compile();
this.setter = setter.Compile();
}
public T Value
{
get { return getter(); }
set { setter(value); }
}
}
public static void Fill(this SomeClass c, params Ref<object>[] p)
//assign inside
object i = 0, i2 = 0, i3 = 0;
c.Fill(Ref.Create(() => i, () => i2, () => i3));
//i, i2 etc changed
Few things to note:
All the above approaches are basically ref approaches, compiler doesn't simply force assigning value of parameters inside the method before the control leaves as in the case of out which is your question, but as far as I know out is not possible here.
I like the first one, simple, and conventional. If not possible my vote is for 3rd approach.
As others have talked about, you can only pass the exact same type as ref/out parameters. So if your method by definition takes arbitrary references of object type, you have to declare even your variables as object locally. In the last approach, you can make the whole thing generic like by changing parameter type to Ref<T> from Ref<object> but that means your all local variables should also be one T.
You can use a dictionary structure to cache Ref<T> to avoid recompiling same trees.
The same implementation can be used to pass properties and variables as method arguments or return values by reference.
As others have said, you can't use params and out together. You have to construct an array at the call site.
This is because params tells the compiler to do the same thing - construct an array from the specified arguments. Unfortunately, when the compiler creates the array, you don't get a reference to it; even if the variable is written with a new array, you can never get to it.
I would guess you are asking for a thin metal ruler. What problem are you trying to solve with this mechanism?
I think I might have an answer to your question; Consider the following code snippet, with the main "InvokeMemberMethod" function doing the job you ask for. I encountered the same problem as you and came up with this solution:
Note: the "isOutXX" parameter specifies if the preceeding parameter is an "out" parameter.
static object InvokeMemberMethod(object currentObject, string methodName, int argCount,
ref object arg1, bool isOut1,
ref object arg2, bool isOut2,
ref object arg3, bool isOut3,
ref object arg4, bool isOut4,
ref object arg5, bool isOut5,
ref object arg6, bool isOut6)
{
if (string.IsNullOrEmpty(methodName))
{
throw new ArgumentNullException("methodName");
}
if (currentObject == null)
{
throw new ArgumentNullException("currentObject");
}
Type[] argTypes = null;
object[] args = null;
if (argCount > 0)
{
argTypes = new Type[argCount];
args = new object[argCount];
argTypes[0] = arg1.GetType();
if (isOut1)
{
argTypes[0] = arg1.GetType().MakeByRefType();
}
args[0] = arg1;
if (argCount == 2)
{
argTypes[1] = arg2.GetType();
if (isOut2)
{
argTypes[1] = arg2.GetType().MakeByRefType();
}
args[1] = arg2;
}
if (argCount == 3)
{
argTypes[2] = arg3.GetType();
if (isOut3)
{
argTypes[2] = arg3.GetType().MakeByRefType();
}
args[2] = arg3;
}
if (argCount == 4)
{
argTypes[3] = arg4.GetType();
if (isOut4)
{
argTypes[3] = arg4.GetType().MakeByRefType();
}
args[3] = arg4;
}
if (argCount == 5)
{
argTypes[4] = arg5.GetType();
if (isOut5)
{
argTypes[4] = arg5.GetType().MakeByRefType();
}
args[4] = arg5;
}
if (argCount == 6)
{
argTypes[5] = arg6.GetType();
if (isOut6)
{
argTypes[5] = arg6.GetType().MakeByRefType();
}
args[5] = arg6;
}
}
MethodInfo methodInfo = currentObject.GetType().GetMethod(methodName, argTypes);
int retryCount = 0;
object ret = null;
bool success = false;
do
{
try
{
//if (methodInfo is MethodInfo)
{
Type targetType = currentObject.GetType();
ParameterInfo[] info = methodInfo.GetParameters();
ParameterModifier[] modifier = new ParameterModifier[] { new ParameterModifier(info.Length) };
int i = 0;
foreach (ParameterInfo paramInfo in info)
{
if (paramInfo.IsOut)
{
modifier[0][i] = true;
}
i++;
}
ret = targetType.InvokeMember(methodName, BindingFlags.InvokeMethod, null, currentObject, args,
modifier, null, null);
//ret = ((MethodInfo)methodInfo).Invoke(currentObject, args,);
success = true;
}
//else
{
// log error
}
}
catch (TimeoutException ex)
{
}
catch (TargetInvocationException ex)
{
throw;
}
retryCount++;
} while (!success && retryCount <= 1);
if (argCount > 0)
{
if (isOut1)
{
arg1 = args[0];
}
if (argCount == 2)
{
if (isOut2)
{
arg2 = args[1];
}
}
if (argCount == 3)
{
if (isOut3)
{
arg3 = args[2];
}
}
if (argCount == 4)
{
if (isOut4)
{
arg4 = args[3];
}
}
if (argCount == 5)
{
if (isOut5)
{
arg5 = args[4];
}
}
if (argCount == 6)
{
if (isOut6)
{
arg6 = args[5];
}
}
}
return ret;
}
public int OutTest(int x, int y)
{
return x + y;
}
public int OutTest(int x, out int y)
{
y = x + 1;
return x+2;
}
static void Main(string[] args)
{
object x = 1, y = 0, z = 0;
Program p = new Program();
InvokeMemberMethod(p, "OutTest", 2,
ref x, false,
ref y, true,
ref z, false,
ref z, false,
ref z, false,
ref z, false);
}
You could pass an array by ref.
Edit:
This would of course change your calling method:
object[] array = new object[] { i, i2, sz, i3, sz2 };
c.Fill(ref array);
I don't imagine the original questioner needs this answer 11 years later, but I found this old question when searching for an overlapping requirement... and thinking through the answer to this question made me realise why my related one wouldn't work very neatly.
For this question, it was implied that the caller needs to communicate the number of parameters and their types to the Fill(...) function - how else does it match up the types to the calling site?
The intended syntax at the calling site could be achieved like this:
public class SomeClass { }
public static void Fill(this SomeClass c, Type[] outputTypes, out object[] p)
{
p = new object[outputTypes.Length];
// TODO: implementation to fill array with values of the corresponding types.
}
// this overload can be removed if "fill" of an empty array is meaningless.
public static void Fill(this SomeClass c)
{
c.Fill(new Type[0], out _);
}
public static void Fill<T>(this SomeClass c, out T r1)
{
c.Fill(new[] { typeof(T) }, out var p);
r1 = (T)p[0];
}
public static void Fill<T1, T2>(this SomeClass c, out T1 r1, out T2 r2)
{
c.Fill(new[] { typeof(T1), typeof(T2) }, out var p);
r1 = (T1)p[0];
r2 = (T2)p[1];
}
// ... extend as required depending on maximum number of out parameters that might be needed
// in particular the 5-parameter version is included in this sample to make OP's sample code line work.
public static void Fill<T1, T2, T3, T4, T5>(this SomeClass c, out T1 r1, out T2 r2, out T3 r3, out T4 r4, out T5 r5)
{
c.Fill(new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5) }, out var p);
r1 = (T1)p[0];
r2 = (T2)p[1];
r3 = (T3)p[2];
r4 = (T4)p[3];
r5 = (T5)p[4];
}
public static void someFunction()
{
SomeClass c = new SomeClass();
int i, i2, i3;
string sz, sz2;
// the line below is exactly as shown in question.
c.Fill(out i, out i2, out sz, out i3, out sz2);
}

How to convert delegate to object in C#?

I am using reflection class to invoke some methods which are on the some other dll.
And one of the methods' parameters are type of delegate.
And I want to invoke this methods by using reflection.
So I need to pass function parameters as object array, but I could not find anything about
how to convert delegate to object.
Thanks in advance
A delegate is an object. Just create the expected delegate as you would normally, and pass it in the parameters array. Here is a rather contrived example:
class Mathematician {
public delegate int MathMethod(int a, int b);
public int DoMaths(int a, int b, MathMethod mathMethod) {
return mathMethod(a, b);
}
}
[Test]
public void Test() {
var math = new Mathematician();
Mathematician.MathMethod addition = (a, b) => a + b;
var method = typeof(Mathematician).GetMethod("DoMaths");
var result = method.Invoke(math, new object[] { 1, 2, addition });
Assert.AreEqual(3, result);
}
Instances of delegates are objects, so this code works (C#3 style) :
Predicate<int> p = (i)=> i >= 42;
Object[] arrayOfObject = new object[] { p };
Hope it helps !
Cédric
Here's an example:
class Program
{
public delegate void TestDel();
public static void ToInvoke(TestDel testDel)
{
testDel();
}
public static void Test()
{
Console.WriteLine("hello world");
}
static void Main(string[] args)
{
TestDel testDel = Program.Test;
typeof(Program).InvokeMember(
"ToInvoke",
BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static,
null,
null,
new object[] { testDel });
}
}
I think this blog post:
C# Reflection - Dealing with Remote Objects
answers your question perfectly.
you can see a delegate as variable type "function". the delegate describes the parameters and return value for a matching function.
delegate void Foo(int a); // here a new delegate obj type Foo has been declared
the above example allows 'Foo' to be used as a data type, the only allowed object that can be matched with a variable of type Foo data type is a method with the same signature so:
void MyFunction(int x);
Foo D = MyFunction; // this is OK
void MyOtherFunction(string x);
Foo D = MyOtherFunction; // will yield an error since not same signature.
Once you have assigned a method to a delegate, you can invoke the method via the delegate:
int n = 1;
D( n ); // or D.Invoke( n );

Categories

Resources