Pass Method as Parameter using C#

后端 未结 12 1365
不知归路
不知归路 2020-11-21 23:30

I have several methods all with the same parameter types and return values but different names and blocks. I want to pass the name of the method to run to another method tha

相关标签:
12条回答
  • 2020-11-22 00:10

    While the accepted answer is absolutely correct, I would like to provide an additional method.

    I ended up here after doing my own searching for a solution to a similar question. I am building a plugin driven framework, and as part of it I wanted people to be able to add menu items to the applications menu to a generic list without exposing an actual Menu object because the framework may deploy on other platforms that don't have Menu UI objects. Adding general info about the menu is easy enough, but allowing the plugin developer enough liberty to create the callback for when the menu is clicked was proving to be a pain. Until it dawned on me that I was trying to re-invent the wheel and normal menus call and trigger the callback from events!

    So the solution, as simple as it sounds once you realize it, eluded me until now.

    Just create separate classes for each of your current methods, inherited from a base if you must, and just add an event handler to each.

    0 讨论(0)
  • 2020-11-22 00:11

    In order to provide a clear and complete answer, I'm going to start from the very beginning before coming up with three possible solutions.


    A brief introduction

    All languages that run on top of CLR (Common Language Runtime), such as C#, F#, and Visual Basic, work under a VM that runs higher level code than machine code, which native languages like C and C++ are compiled to. It follows that methods aren't Assembly subroutines, nor are they values, unlike JavaScript as well as most functional languages; rather, they're definitions that CLR recognizes. Thus, you cannot think to pass a method as a parameter, because methods don't produce any values themselves, as they're not expressions but statements, which are stored in the generated assemblies. At this point, you'll face delegates.


    What's a delegate?

    A delegate represents a pointer to a method. As I said above, a method is not a value, hence there's a special class in CLR languages, namely Delegate, which wraps up any method.

    Look at the following example:

    static void MyMethod()
    {
        Console.WriteLine("I was called by the Delegate special class!");
    }
    
    static void CallAnyMethod(Delegate yourMethod)
    {
        yourMethod.DynamicInvoke(new object[] { /*Array of arguments to pass*/ });
    }
    
    static void Main()
    {
        CallAnyMethod(MyMethod);
    }
    

    Three different solutions, the same concept behind:

    • The type–unsafe way
      Use the Delegate special class directly as the example above. The problem of this solution is that your code will be unchecked as you pass dynamically your arguments without restricting them to the types of those in the method definition.

    • The custom way
      Besides the Delegate special class, the delegate concept spreads to custom delegates, which are declarations of methods preceded by the delegate keyword. They are thereby type–checked the same as method declarations, so you'll come up with flawlessly safe code.

      Here's an example:

      delegate void PrintDelegate(string prompt);
      
      static void PrintSomewhere(PrintDelegate print, string prompt)
      {
          print(prompt);
      }
      
      static void PrintOnConsole(string prompt)
      {
          Console.WriteLine(prompt);
      }
      
      static void PrintOnScreen(string prompt)
      {
          MessageBox.Show(prompt);
      }
      
      static void Main()
      {
          PrintSomewhere(PrintOnConsole, "Press a key to get a message");
          Console.Read();
          PrintSomewhere(PrintOnScreen, "Hello world");
      }
      
    • The standard library's way
      Alternatively, you can use a delegate that's part of the .NET Standard:

      • Action wraps up a parameterless void method.
      • Action<T1> wraps up a void method with one parameter of type T1.
      • Action<T1, T2> wraps up a void method with two parameters of types T1 and T2, respectively.
      • And so forth...
      • Func<TR> wraps up a parameterless function with TR return type.
      • Func<T1, TR> wraps up a function with TR return type and with one parameter of type T1.
      • Func<T1, T2, TR> wraps up a function with TR return type and with two parameters of types T1 and T2, respectively.
      • And so forth...

      However, bear in mind that using predefined delegates like this, parameter names don't describe what they have to be passed in, nor is the delegate name meaningful on what it's supposed to do. Therefore, be cautions about when to use these delegates and refrain from using them in contexts where their purpose is not perfectly self–evident.

    The latter solution is the one most people posted. I'm still mentioning it in my answer for completeness.

    0 讨论(0)
  • 2020-11-22 00:11

    You should use a Func<string, int> delegate, that represents a function taking a string argument and returning an int value:

    public bool RunTheMethod(Func<string, int> myMethod)
    {
        // Do stuff
        myMethod.Invoke("My String");
        // Do stuff
        return true;
    }
    

    Then invoke it this way:

    public bool Test()
    {
        return RunTheMethod(Method1);
    }
    
    0 讨论(0)
  • 2020-11-22 00:11
    class PersonDB
    {
      string[] list = { "John", "Sam", "Dave" };
      public void Process(ProcessPersonDelegate f)
      {
        foreach(string s in list) f(s);
      }
    }
    

    The second class is Client, which will use the storage class. It has a Main method that creates an instance of PersonDB, and it calls that object’s Process method with a method that is defined in the Client class.

    class Client
    {
      static void Main()
      {
        PersonDB p = new PersonDB();
        p.Process(PrintName);
      }
      static void PrintName(string name)
      {
        System.Console.WriteLine(name);
      }
    }
    
    0 讨论(0)
  • 2020-11-22 00:13
    public static T Runner<T>(Func<T> funcToRun)
    {
        //Do stuff before running function as normal
        return funcToRun();
    }
    

    Usage:

    var ReturnValue = Runner(() => GetUser(99));
    
    0 讨论(0)
  • 2020-11-22 00:13

    If you want to pass Method as parameter, use:

    using System;
    
    public void Method1()
    {
        CallingMethod(CalledMethod);
    }
    
    public void CallingMethod(Action method)
    {
        method();   // This will call the method that has been passed as parameter
    }
    
    public void CalledMethod()
    {
        Console.WriteLine("This method is called by passing parameter");
    }
    
    0 讨论(0)
提交回复
热议问题