Pasar el Método como Parámetro usando C#


Tengo varios métodos todos con la misma firma (parámetros y valores de retorno) pero diferentes nombres y las partes internas de los métodos son diferentes. Quiero pasar el nombre del método a ejecutar a otro método que invoque el método passed in.

public int Method1(string)
{
    ... do something
    return myInt;
}

public int Method2(string)
{
    ... do something different
    return myInt;
}

public bool RunTheMethod([Method Name passed in here] myMethodName)
{
    ... do stuff
    int i = myMethodName("My String");
    ... do more stuff
    return true;
}

public bool Test()
{
    return RunTheMethod(Method1);
}

Este código no funciona, pero esto es lo que estoy tratando de hacer. Lo que no entiendo es cómo escribir el código RunTheMethod ya que necesito definir el parámetro.

Author: carefulnow1, 2010-01-18

10 answers

Puede usar el delegado Func en.net 3.5 como parámetro en su método RunTheMethod. El delegado Func permite especificar un método que toma un número de parámetros de un tipo específico y devuelve un único argumento de un tipo específico. Aquí hay un ejemplo que debería funcionar:

public class Class1
{
    public int Method1(string input)
    {
        //... do something
        return 0;
    }

    public int Method2(string input)
    {
        //... do something different
        return 1;
    }

    public bool RunTheMethod(Func<string, int> myMethodName)
    {
        //... do stuff
        int i = myMethodName("My String");
        //... do more stuff
        return true;
    }

    public bool Test()
    {
        return RunTheMethod(Method1);
    }
}
 666
Author: Egil Hansen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-17 21:08:10

Necesitas usar un delegado . En este caso, todos sus métodos toman un parámetro string y devuelven un int - esto es simplemente representado por el delegado Func<string, int> 1. Así que su código puede ser correcto con un cambio tan simple como este:

public bool RunTheMethod(Func<string, int> myMethodName)
{
    // ... do stuff
    int i = myMethodName("My String");
    // ... do more stuff
    return true;
}

Los delegados tienen mucho más poder que esto, es cierto. Por ejemplo, con C# puede crear un delegado a partir de una expresión lambda , por lo que podría invocar su método de esta manera:

RunTheMethod(x => x.Length);

Que creará un función anónima como esta:

// The <> in the name make it "unspeakable" - you can't refer to this method directly
// in your own code.
private static int <>_HiddenMethod_<>(string x)
{
    return x.Length;
}

Y luego pasar ese delegado al método RunTheMethod.

Puede usar delegados para suscripciones de eventos, ejecución asincrónica, devoluciones de llamada, todo tipo de cosas. Vale la pena leer sobre ellos, particularmente si desea usar LINQ. Tengo un artículoque es en su mayoría sobre las diferencias entre delegados y eventos, pero puede que lo encuentre útil de todos modos.


1 Esto solo se basa en la genérico Func<T, TResult> delegate type en el framework; usted podría declarar fácilmente el suyo propio:

public delegate int MyDelegateType(string value)

Y luego haga que el parámetro sea de tipo MyDelegateType en su lugar.

 311
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-26 05:31:29

¡También puedes probar Action Delegate!

 public static int Method1(string mystring)
 {
      return 1;
 }

 public static int Method2(string mystring)
 {
     return 2;
 }

 public bool RunTheMethod(Action myMethodName)
 {
      myMethodName();
      return true;
 }

Y luego llama a tu método usando

RunTheMethod(() => Method1("MyString1"));

O

public static object InvokeMethod(Delegate method, params object[] args)
{
     return method.DynamicInvoke(args);
}

Luego simplemente llame al método

Console.WriteLine(InvokeMethod(new Func<string,int>(Method1), "MyString1"));

Console.WriteLine(InvokeMethod(new Func<string, int>(Method2), "MyString2"));
 87
Author: Humble Coder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-17 15:29:44
public static T Runner<T>(Func<T> funcToRun)
{
    //Do stuff before running function as normal
    return funcToRun();
}

Uso:

var ReturnValue = Runner(() => GetUser(99));
 25
Author: kravits88,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-14 02:50:18

Debe usar un delegado Func<string, int>, que representa una función que toma un string como argumento y devuelve un int:

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

Entonces úsalo:

public bool Test() {
    return RunTheMethod(Method1);
}
 10
Author: Bruno Reis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-23 21:37:09

Si desea la capacidad de cambiar qué método se llama en tiempo de ejecución, le recomendaría usar un delegado: http://www.codeproject.com/KB/cs/delegates_step1.aspx

Le permitirá crear un objeto para almacenar el método a llamar y puede pasarlo a sus otros métodos cuando sea necesario.

 6
Author: MadcapLaugher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-17 21:05:52

Si bien la respuesta aceptada es absolutamente correcta, me gustaría proporcionar un método adicional.

Terminé aquí después de hacer mi propia búsqueda de una solución a una pregunta similar. Estoy construyendo un framework impulsado por plugins, y como parte de él quería que las personas pudieran agregar elementos de menú al menú de aplicaciones a una lista genérica sin exponer un objeto Menu real porque el framework puede implementarse en otras plataformas que no tienen objetos de interfaz de usuario Menu. Añadir información general sobre el menu es bastante fácil, pero permitir al desarrollador del plugin suficiente libertad para crear la devolución de llamada para cuando se hace clic en el menú estaba demostrando ser un dolor. Hasta que me di cuenta de que estaba tratando de reinventar la rueda y los menús normales llamar y activar la devolución de llamada de los eventos!

Así que la solución, tan simple como suena una vez que te das cuenta, me eludió hasta ahora.

Simplemente cree clases separadas para cada uno de sus métodos actuales, heredadas de una base si es necesario, y simplemente agregue un evento manejador de cada uno.

 2
Author: Wobbles,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-29 13:48:34

Para compartir una solución lo más completa posible, voy a terminar presentando tres formas diferentes de hacer, pero ahora voy a comenzar desde el principio más básico.


Breve introducción

Todos los lenguajes CLR (Common Language Runtime) (como C# y Visual Basic) funcionan bajo una máquina virtual llamada CLI (Common Language Interpreter) que ejecuta el código en un nivel más alto que los lenguajes nativos como C y C++ (que codificar). Se deduce que los métodos no son ningún tipo de bloque compilado, sino que son solo elementos estructurados que CLR reconoce y usa para extraer su cuerpo y volverlo a las instrucciones en línea del código máquina. Por lo tanto, no se puede pensar en pasar un método como un parámetro, porque un método no produce ningún valor por sí mismo: ¡no es una expresión válida! Así que, vas a tropezar con el concepto de delegado.


¿Qué es un delegado?

Un delegado representa un puntero a un método. Debido a que (como dije anteriormente) un método no es un valor, hay una clase especial en los lenguajes CLR: Delegate. Esa clase envuelve cualquier método y puedes implícitamente convertir cualquier método a eso.

Mira el siguiente ejemplo de uso:

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);
}

Los tres caminos:

  • Camino 1
    Utilice la clase especial Delegate directamente como el ejemplo anterior. El problema de esta solución es que su código será desmarcado a medida que pase dinámicamente su argumentos sin restringirlos a los tipos de aquellos en la declaración de método.

  • Vía 2/3 Además de la clase especial Delegate, el concepto delegates se extiende a delegados personalizados, que son declaraciones de métodos precedidas por la palabra clave delegate y se comportan como un método normal. Están tan comprobados, y llegarás a un código "perfecto".

Mira el siguiente ejemplo:

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");
}

Una segunda opción de esta manera para no escribir su propio delegado personalizado está utilizando uno de ellos declarado dentro de las bibliotecas del sistema:

  • Action envuelve un void sin argumentos.
  • Action<T1> envuelve un void con un argumento.
  • Action<T1, T2> envuelve a void con dos argumentos.
  • Y así sucesivamente...
  • Func<TR> envuelve una función con TR tipo de retorno y sin argumentos.
  • Func<TR, T1> envuelve una función con el tipo de retorno TR y con un argumento.
  • Func<TR, T1, T2> envuelve una función con TR devuelve el tipo y con dos argumentos.
  • Y así sucesivamente...

(Esta última solución es que mucha gente publicó.)

 2
Author: Davide Cannizzo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-15 14:35:52

Aquí hay un ejemplo que puede ayudarlo a comprender mejor cómo pasar una función como parámetro.

Supongamos que tiene la página Padre y desea abrir una ventana emergente secundaria. En la página principal hay un cuadro de texto que debe llenarse basándose en el cuadro de texto emergente hijo.

Aquí necesita crear un delegado.

Padre.cs // declaración de los delegados nombre de relleno del vacío del delegado público (String FirstName);

Ahora cree una función que llenará su cuadro de texto y función deben mapear delegados

//parameters
public void Getname(String ThisName)
{
     txtname.Text=ThisName;
}

Ahora haga clic en el botón que necesita para abrir una ventana emergente Hijo.

  private void button1_Click(object sender, RoutedEventArgs e)
  {
        ChildPopUp p = new ChildPopUp (Getname) //pass function name in its constructor

         p.Show();

    }

EN el constructor ChildPopUp necesitas crear el parámetro ' delegate type 'de la página principal / /

ChildPopUp.cs

    public  Parent.FillName obj;
    public PopUp(Parent.FillName objTMP)//parameter as deligate type
    {
        obj = objTMP;
        InitializeComponent();
    }



   private void OKButton_Click(object sender, RoutedEventArgs e)
    {


        obj(txtFirstName.Text); 
        // Getname() function will call automatically here
        this.DialogResult = true;
    }
 1
Author: Shrikant-Divyanet Solution,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-25 09:58:29

Aquí hay un ejemplo sin un parámetro: http://en.csharp-online.net/CSharp_FAQ:_How_call_a_method_using_a_name_string

Con parámetros: http://www.daniweb.com/forums/thread98148.html #

Básicamente pasa una matriz de objetos junto con el nombre del método. a continuación, utilice ambos con el método Invoke.

Params Object [] parámetros

 0
Author: Jeremy Samuel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-17 21:04:36