7

I need to be able to pass an arbitrary method to some function myFunction:

void myFunction(AnyFunc func) { ... }

It should be possible to execute it with other static, instance, public or private methods or even delegates:

myFunction(SomeClass.PublicStaticMethod);
myFunction(SomeObject.PrivateInstanceMethod);
myFunction(delegate(int x) { return 5*x; });

Passed method may have any number of parameters and any return type. It should also be possible to learn the actual number of parameters and their types in myFunction via reflection. What would be AnyFunc in the myFunction definition to accommodate such requirements? It is acceptible to have several overloaded versions of the myFunction.

Sergiy Belozorov
  • 5,856
  • 7
  • 40
  • 73
  • What do you expect to pass to the method if it does have parameters? It sounds like you should be converting whatever the method is to a no parameter/void return (aka `Action`) before passing it to this method. – Servy Apr 10 '13 at 16:25
  • `myFunction` will analyze parameters of the function, check them against some external database and call it with necessary values or fail if no matching entry in database is found. – Sergiy Belozorov Apr 10 '13 at 16:26
  • That's a *very* worrying design, in which you're essentially treating code as data. You should consider trying to redesign this such that you code is static. – Servy Apr 10 '13 at 16:27
  • Yes. There will be a compiler embedded that will construct code from data. I am aware of the security risks, but this is what we need in our project. – Sergiy Belozorov Apr 10 '13 at 16:31
  • @Servy no, it isn't worrying design, it's just another design — treating a functions as a data. Common Lisp, Haskell, these languages are built around such an architecture. Specifically to C#: I'm just writing a function that takes a text as a first argument, and applies a function from the second argument to an every newline. Do you really think it's a bad idea? It makes a design transparent. – Hi-Angel Dec 23 '14 at 16:26
  • 1
    @Hi-Angel That's dealing with methods of a known signature. Having an unknown function that accepts a string and returns a specific type of result, without knowing it's implementation, is certainly not bad design. – Servy Jan 02 '15 at 15:45

2 Answers2

7

The Delegate type is the supertype of all other delegate types:

void myFunction(Delegate func) { ... }

Then, func.Method will give you a MethodInfo object you can use to inspect the return type and parameter types.

When calling the function you will have to explicitly specify which type of delegate you want to create:

myFunction((Func<int, int>) delegate (int x) { return 5 * x; });

Some idea of what you're trying to accomplish at a higher level would be good, as this approach may not turn out to be ideal.

cdhowie
  • 158,093
  • 24
  • 286
  • 300
  • I would like to make the call site as simple as possible and hide the gritty details inside of the library. Any way to specify the `myFunction` so that users don't need to cast their delegates? – Sergiy Belozorov Apr 10 '13 at 16:28
  • 1
    @SergiyByelozyorov The call site cannot possibly be made any simpler, as the compiler needs *some* concrete delegate type to create an instance of. If you want to accept "any delegate type" then the caller *must* specify a delegate type themselves. (For example, given the anonymous delegate `delegate () { }`, both [`Action`](http://msdn.microsoft.com/en-us/library/system.action.aspx) and [`ThreadStart`](http://msdn.microsoft.com/en-us/library/system.threading.threadstart.aspx) are compatible delegate types -- how is the compiler to know which one to create?) – cdhowie Apr 10 '13 at 16:29
  • Thanks. I guess that's my answer then. That's not possible. Thank you. – Sergiy Belozorov Apr 10 '13 at 16:32
  • But why does compiler need to know exact delegate type? Isn't it sufficient just to know parameter types that are specified in the delegate-syntax? Is return value type a problem? Can't compiler derive it from the body? – Sergiy Belozorov Apr 10 '13 at 16:48
  • @SergiyByelozyorov That's like trying to construct an object and asking "isn't it enough to know the type of arguments the constructor takes?" No, it's not, because there are many types in the framework that have the same constructor signature. You have to specify one. Thought experiment: given the definitions `delegate void A(); delegate void B();`, how would you expect the compiler to infer the type of the expression `delegate () {}` with `Delegate` being the only contextual clue about the type of the expression? – cdhowie Apr 10 '13 at 16:52
  • @SergiyByelozyorov The reason you can usually omit the type is because of type inference. If you have `void Foo(Func func);` and you write `Foo(x => x*2);` the compiler knows that the desired type of the lambda expression is `Func`. There is an implicit conversion from lambdas/anonymous-methods to a specific delegate type. There is no implicit conversion to `Delegate`. – cdhowie Apr 10 '13 at 16:54
  • Could you elaborate the syntax meaning on the `myFunction((Func) delegate (int x) { return 5 * x; });` line? Without the (Func) section it is not even a delegate type... – Daniel May 17 '17 at 21:50
  • 1
    @Harb `(Func)` is a cast to a delegate type. This gives the compiler enough context to infer that the type of the anonymous delegate should be `Func`. – cdhowie May 17 '17 at 22:03
  • @cdhowie as I understand `delegate (int x) { return 5 * x; }` is basically (resolved to be) a method. Thus, it is not a delegate, so it cast to the specific delegate type as you mentioned. If I understood correctly this is a syntax to define an anonymous method. – Daniel May 17 '17 at 22:09
  • @Harb That's mostly correct but it's a bit more complex than that. The contextual information about what type of delegate is expected can affect compilation of the delegate. For example, you can just as easily say `(Func) delegate (int x) { return 5 * x; }` and suddenly the delegate refers to a method that returns `float` -- the contextual delegate type has informed the compiler what the return type of the anonymous method should be! [See here](http://ideone.com/Uuwc2W) – cdhowie May 17 '17 at 22:20
0

Have the method accept a Delegate, rather than a particular delegate:

void myFunction(Delegate func)
{

}
Servy
  • 202,030
  • 26
  • 332
  • 449