1

This is a follow up question to How do I pass a function pointer delegate for a different class in c#

I have a class

public class ClassA
{
    public void Foo()
    {
        Console.WriteLine("Foo()");
    }
    public void Foo(int x, int y)
    {
        Console.WriteLine("Foo(" + x.ToString() + ", " + y.ToString()  + ")" );
    }
    public void Foo(int x, int y, int z)
    {
        Console.WriteLine("Foo(" + x.ToString() + ", " + y.ToString() + ", " + z.ToString() + ")" );
    }
}

In another method, I would like to call the class functions of classA like this:

ClassA obj = new ClassA();
TakesFun(obj.Foo);
TakesFun(obj.Foo, 1, 2);
TakesFun(obj.Foo, 1, 2, 3);

What should be the syntax of TakesFun? I would like to make it generic to take in many/any/none arguments and call the appropriate function based on the number of arguments?

Obviously below function syntax is wrong, but I am looking for one function similar to that.

public static void TakesFun<TParam>(Action<TParam[]> action, params TParam[] paramList)
{
    Console.WriteLine("TakesFun");
    action(paramList);
}

EDIT1: ClassA Foo functions were just an example. I would like to call more complicated functions which can take any kind of argument. Like for eg:

public void CleanList(List<int> l)
{
    l.Clear();
}
sgowd
  • 946
  • 3
  • 10
  • 27
  • 3
    Why make this so complicated?? Just add properties x, y and z as nullable ints to ClassA and if x,y, and z are null, call Foo(), if z is null call Foo(x, y) and if z is not null call Foo(x, y, z) – Ryan Wilson Jun 18 '18 at 19:03
  • 2
    Yeah this seems like a really bad design. Optional parameters are a thing, use them – maccettura Jun 18 '18 at 19:04
  • 1
    Could you please clarify how? I am asking this because I am new to C#. – sgowd Jun 18 '18 at 19:09
  • I am writing a thread dispatcher equivalent for a non-WPF and nonGUI application and it is essential that I have a function syntax for a BeginInvoke that is to be done in similar lines as TakesFun wihch can take an action and arguments and then run the function at a later time.. I have updated the listing with EDIT1 that shows more of what kinds of function that I would like to run. I cant be using optional arguments for ClassA functions. More detail on the background is in the link in the original post. – sgowd Jun 18 '18 at 19:21
  • huh? use callbacks/delegates. why over complicate things.. simple is the most difficult engineering tenet to master. – T McKeown Jun 18 '18 at 19:26
  • What's wrong with your method `TakesFun(Action action, params TParam[] paramList)` ? – vendettamit Jun 18 '18 at 19:29
  • @vendettamit, Compilation fails on invokation using TakesFun(obj.Foo, 1, 2). – sgowd Jun 18 '18 at 19:31

1 Answers1

6

In another method, I would like to call the class functions of classA like this: TakesFun(obj.Foo);

You go on to note that TakesFun is a single method that takes an Action, rather than a collection of overloaded methods.

You can't always get what you want, and you'll have to learn to live with the disappointment. C# does not support the feature you want.

You are converting obj.Foo from a method group form to a delegate form, and that requires that C# performs overload resolution by comparing the delegate in the formal parameter list of TakesFun with the collection of methods named Foo, and then choose the best one.

Since there is no "best one" at compile time, this will fail.

Find another way to solve your problem. Your question sounds very much like what we call an "XY problem". That is, you have some real problem, you have a crazy idea about how to solve it that will never work, and now you're asking a question about your crazy idea. Ask a question about the real problem. There's a better way to solve it than this, almost certainly.

Now, if we relax some of the constraints of your problem then we can do it. In particular, if we relax the constraint that there be only one TakesFun and that it be variadic, then we can do it. I note that TakesFun is in fact the function application operation:

static void Apply(Action action) => action();
static void Apply<A>(Action<A> action, A a) => action(a);
static void Apply<A, B>(Action<A, B> action, A a, B b) => action(a, b);
static void Apply<A, B, C>(Action<A, B, C> action, A a, B b, C c) => action(a, b, c);
... and so on, as many as you need

And now the magic of overload resolution solves your problem:

Apply(obj.Foo, 1, 2, 3); // Works fine

Overload resolution deduces that Apply means Apply<int, int, int>, that the Action is Action<int, int, int> and that the Foo meant is the one that takes three integers.

C# type inference is pretty good, but you have to use it within the bounds that we designed into the language.


UPDATE: Based on your comments it sounds like you are trying to re-invent the task parallel library. I would investigate the TPL to see if it already meets your needs.

In C# we represent the notion of a unit of work that will be performed in the future as Task; there is a huge library and built-in language support for composing workflows out of tasks, scheduling their continuations to various threads, and so on. Use this work rather than attempting to invent it yourself.

If for example you wanted to create unstarted tasks that represent future work you would do it like this:

static Task TaskFactory(Action action) => new Task(action);
static Task TaskFactory<A>(Action<A> action, A a) => new Task(()=>action(a));
static Task TaskFactory<A, B>(Action<A, B> action, A a, B b) => new Task(()=>action(a, b));

and so on. Now you can do:

Task t = TaskFactory(obj.Foo, 1, 2);

and get back a t that when started will execute the action. You can then say what you want to happen when the task is complete:

t.ContinueWith(task => Console.WriteLine("Task complete!"));

and then start it up:

t.Start();

There are mechanisms for creating tasks that are already started and run on worker threads. There are mechanisms for scheduling tasks or their completions to run on particular threads. And so on; this is a huge topic. Ask another question if you have questions about that.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Yucks! In c++, It is so easy to solve this problem using bind. – sgowd Jun 18 '18 at 19:33
  • How do you suggest I proceed? – sgowd Jun 18 '18 at 19:34
  • @sgowd explain your problem in plain english, without the burdens of how you are _trying_ to solve it. Just purely explain the problem at hand. Eric is right to suspect an XY Problem here... – maccettura Jun 18 '18 at 19:35
  • @sgowd: Well then if C++ meets your needs better I suggest that you use it! – Eric Lippert Jun 18 '18 at 19:36
  • 2
    @sgowd: I recommend that you first, explain your problem better. Second, I've described an addition to my answer how you might do this in C# by relaxing the constraints you have unnecessarily put on the problem. – Eric Lippert Jun 18 '18 at 19:36
  • 3
    @sgowd: What's not clear to me is **why are you doing this at all**? There already is a function application operator in C#. Why would you do `TakesFun(obj.Foo, 1, 2, 3)` when you could simply write `obj.Foo(1, 2, 3)`, which is both shorter and easier? – Eric Lippert Jun 18 '18 at 19:37
  • @Eric Lippert, Thanks for the detailed answer. The real problem I am trying to solve involves a BeginInvoke function that is similar to TakesFun function, . BeginInvoke doesn't run the function immediately but it puts the function to be called and its arguments into a queue and the Run function later takes the function to be called and the arguments and runs it. – sgowd Jun 18 '18 at 19:41
  • 1
    @sgowd: **It sounds like you are trying to re-invent async-await and the Task Parallel Library, badly.** C# already has a mechanism for precisely that built into the language. If you want to dispatch asynchronous work onto threads then first, I would investigate if the standard asynchronous worker thread context is already what you want, and if it is not, then investigate how to write your own context. – Eric Lippert Jun 18 '18 at 19:41
  • @sgowd: A function which is going to be called in the future and will complete with a result in the future is represented in C# by `Task` if void-returning and `Task` if it returns a value; there is an entirely library and many language features devoted to the creation of tasks, orchestrating the order in which they are constrained to run, and scheduling of their continuations onto various threads. I suggest you look into it; I think the work you want to do has already been written by experts. – Eric Lippert Jun 18 '18 at 19:46
  • 1
    @Eric Lippert, I will lookup on async-await. What is function application operator? – sgowd Jun 18 '18 at 19:47
  • @sgowd: The function application operator in C# is `()`. That is, if you have a function `f` and an argument `a` then the function may be applied to the argument by using the operator `()` as follows: `f(a)`. Obviously you knew that already, but most people don't even stop to think that this is an *operator* just like `+` or `*`. – Eric Lippert Jun 18 '18 at 19:47
  • @sgowd: I've added some additional notes to get you started. – Eric Lippert Jun 18 '18 at 20:23
  • 1
    @Eric Lippert, Thanks and for also making me aware of the XY problem. Given that most people try to solve problem X or give alternate solutions to problem Y, I realize it is important for askers to state full problem. I always had this problem at work too! – sgowd Jun 18 '18 at 21:38