1

Is there a way in C# to pass a single array as separate arguments (parameters) to a function?

Given the following super simplified example:

Func<int, int, int> sum = (int a, int b) => { return a + b; };
// sum(1, 2); This would work and return 3
sum([1,2]); // This is how I would like to call it

Is there a way to make last line work?

Note: I know that you could make a delegate take a single int[] as an argument, but in my real use case modifying a delegate is not possible.

Note 2: In real use case parameters are not of the same type. It could be a mixture of int, string, object, etc.

If it helps, in PHP this could be achieved with call_user_func_array(). Example

UPDATE: Real use case is that I want to have a single function that receives a delegate and all required parameters. So instead of having to write out all different cases like below:

public void OneParam<T1, TOut>(Func<T1, TOut> func, T1 p1)
{
    // Do some fancy stuff before calling
    func.Invoke(p1);
}

public void TwoParam<T1, T2, TOut>(Func<T1, T2, TOut> func, T1 p1, T2 p2)
{
    // Do exact same some fancy stuff before calling
    func.Invoke(p1, p2);
}

I would like to have just a single function that takes any delegate with any amount of parameters and then eventually to be able to pass those parameters into a delegate.

TL;DR answer: As per @gunr2171 answer this is not possible. I wanted the opposite of params, which is not achievable. This probably make sense as C# creators also had to type out all different delegates with different amount of parameters manually as well.

rCgLT
  • 128
  • 6

3 Answers3

5

Right now, C# does not have the functionality you are desiring. If your method has two parameters, you must call it with two parameters. A collection that has (or, "might have") two elements only counts as a single parameter.

Remember that there is no "array literals" in C#, so the compiler can't guarantee a collection has a particular length at compile-time.


C# has the params keyword. This is the opposite of what you're trying to do. It allows you pass in individual values when you call the method, but those values are treated as a single collection within the method.

void MyMethod(params int[] values) {
  // values is [ 1, 2, 5 ]
}

int someIntVariable = 5;
MyMethod(1, 2, someIntVariable);

You're looking for the reverse of that.


You could do something with reflection, at the cost of only being allowed indirect calls. You'd need to make a wrapper method to call the method on your behalf. In this example, I'm not doing any sort of validation, and types are strictly ints.

public static void Main()
{
    var result = Execute(Sum, new[] { 4, 5 });
    Console.WriteLine(result);
    // prints "9"
}

public static int Sum(int a, int b) => a + b;

public static int Execute(Func<int, int, int> method, ICollection<int> values)
{
    // optional: validation of values length
    return (int)method.DynamicInvoke(values.Cast<object>().ToArray())!;
}

However, of course, this only works if you are ok with making and calling this wrapper method. You're not calling Sum directly. If the end methods you'd like to execute vary in type and parameter count, you'll also need to adjust accordingly.

Probably the biggest disadvantage with this approach is that you lose compile-time guarantees that you are providing your methods with the correct number of parameters.


In my opinion, the sane thing to do here is extract the elements that you want from the collection to serve as individual parameters.

int[] values = new[] { 4, 5 };
Sum(values[0], values[1]);
gunr2171
  • 16,104
  • 25
  • 61
  • 88
1

Short answer:

var result = (int)sum.DynamicInvoke(new Object[]{1,2});

Be aware if arguments mismatch, you will get an exception.

eocron
  • 6,885
  • 1
  • 21
  • 50
-1

If input array is a mixture of different types then a good way is to use params like below

using System;

public class Program
{
    public static void Main()
    {
        HandleDifferentItems(new object[] {1,2,"three", 4});
    }

    static void HandleDifferentItems(params object[] args)
    {
        foreach (var item in args)
        {
            if (item is int)
            {
                Console.WriteLine("Integer :" + item);
            }
            else if(item is string)
            {
                Console.WriteLine("string :" + item);
            }
            else if(item is SomeClass)
            {
                 //Handle some class
            }
        }
    }
}
as-if-i-code
  • 2,103
  • 1
  • 10
  • 19