0

I am looking for a way to unfold a params object array casting to the correct argument type based on a given function signature as demonstrated in the following example:

public class Class {
  public enum State { A, B, /* ...*/}
  public void GenericFunction(State state, params object[] args) {
    switch(state) {
      case State.A: Apply(CaseA,args); break;
      case State.B: Apply(CaseB,args); break;
      /* ... */
    }
  }

  public void CaseA(int i, string s) { /* ... */ }
  public void CaseB(double[] ds) { /* ... */ }

  public void ExampleInvocation() {
    GenericFunction(State.A,10,"abc"); // supposed to call CaseA with arguments 10 and "abc"
    GenericFunction(State.B,new double[] { 1.2, 3.5, 7.2}); // supposed to call CaseB

    GenericFunction(State.A,6.66); // supposed to throw an exception
  }
}

Is there a library or some feature in c# providing something like the method Apply?

MarkusParker
  • 1,264
  • 2
  • 17
  • 35
  • Please explain unfolding, maybe you want just `.Cast()` ? or `OfType` – TheGeneral Sep 20 '18 at 09:09
  • By "unfolding" I mean converting an array of objects to separate arguments of a function. Futhermore the objects should be casted to the corresponding argument type. But the challenge is, that I don't want to do it for each function signature manually for all possible function CaseA, CaseB, ..., but somehow automatically. – MarkusParker Sep 20 '18 at 09:15
  • @MarkusParker: You can't do that without a `switch` which handles every possible case. The reason is that we don't have any informations about the `Case` methods. What do they have in common? – Tim Schmelter Sep 20 '18 at 09:20
  • @TimSchmelter: I do think, it is possible. I guess one way to implement the Apply method is to use type inspection for analysing the arguments of the passed function, then iterate over these arguments trying to match them with the objects in the args variable. I just wonder if it is not done already in some library - or maybe there is an even simpler way. – MarkusParker Sep 20 '18 at 09:25
  • 1
    Aren't you describing method overloading in another but complex way? Apply(int i, string s), Apply(double[] ds)? – Cetin Basoz Sep 20 '18 at 09:32
  • I really don't understand why you just don't call the methods directly in `ExampleInvocation`: `CaseA(10,"abc")` finished. No need for reflection, boxing, error handling or anything else. – Tim Schmelter Sep 20 '18 at 09:44
  • @CetinBasoz: Not exactly. When doing overloading, I would specify the function signature twice: Once for the function CaseX and once for the function overload of Apply. Adding another function CaseC afterwards would imply adding another overload. That I don't want. – MarkusParker Sep 20 '18 at 09:45
  • No, you would only do it for Apply. Instead did separately for CaseA and CaseB and then another intermediate GenericFunction() you would only do Apply. You can name it AllCases if you like. – Cetin Basoz Sep 20 '18 at 09:48
  • What you ask for would be possible if C# had something like [variadic templates](https://en.wikipedia.org/wiki/Variadic_template) in C++ where the number of generic arguments can be a variable number. But thats not possible in C# (which is the reason why for example there are so many overloads for [`Tuple.Create`](https://learn.microsoft.com/de-de/dotnet/api/system.tuple.create?view=netframework-4.7.2)). – Tim Schmelter Sep 20 '18 at 09:52
  • @TimSchmelter: The example I gave is very condensed to demonstrate the technical issue I faced. It is clearly not enough to explain the motivation behind this approach. For this I would need to give much more background information. – MarkusParker Sep 20 '18 at 09:52
  • @MarkusParker: i know, that's the problem. You gave an abstract example which is enough to demonstrate what you think that you need. But it's not enough to suggest you a different approach. That's the classic [XY-problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). – Tim Schmelter Sep 20 '18 at 09:56

2 Answers2

1

You can do that using reflection. The price is performance and no compile time checks.

 typeof(MyType).GetMethod("add").Invoke(null, new [] {arg1, arg2})

Example taken from: how to dynamically call a function in c#

To decide which function to use I'd inspect the available functions using reflection GetParameters() (see https://learn.microsoft.com/en-us/dotnet/api/system.reflection.methodbase.getparameters) and cache the result to a Dictionary.

As others already mentioned: While this can be a valid way in special cases - in most cases it's not the way you should do it in C#.

Christoph Lütjen
  • 5,403
  • 2
  • 24
  • 33
  • Even with reflection, how you know the method `add`? He doesn't want a `switch` to determine the method name. As you can see there are multiple methods. I'm pretty sure that he asks for the wrong way instead of asking us what way was better. But we know too little to provide something helpful – Tim Schmelter Sep 20 '18 at 09:36
  • @TimSchmelter - you're right, I thought he was asking for "how to write JavaScripts function.apply(args) in c#". Added minimum info to answer, but you are right, for a "good" solution we've not enough information. – Christoph Lütjen Sep 20 '18 at 10:23
0

So far I ended up doing something like

private void Apply(string functionName, object[] args) {
  var methodInfo = typeof(Class).GetMethod(functionName, BindingFlags.Instance,
                    Type.DefaultBinder, args.Select(_ => _.GetType()).ToArray(), null);
  if (methodInfo == null) {
    // error handling - throw appropriate exception
  } else {
    methodInfo.Invoke(this, args);
  }
}

This requires to change the original call of Apply(CaseA,args) to Apply(nameof(CaseA),args).

Any more elegant solution is still welcome.

MarkusParker
  • 1,264
  • 2
  • 17
  • 35