Reputation: 1044
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();
}
Upvotes: 1
Views: 234
Reputation: 660493
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.
Upvotes: 6