Pasar método como parámetro usando C #

Tengo varios métodos, todos con los mismos tipos de parámetros y valores de retorno, pero con diferentes nombres y bloques. Quiero pasar el nombre del método a ejecutar a otro método que invocará el método pasado.

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.

preguntado el 17 de enero de 10 a las 18:01

¿Por qué no pasa un delegado en lugar del nombre del método? -

La firma del método de reclamaciones de pregunta trata sobre parámetros y valores de retorno, cuando en realidad comprende tipos de parámetros y nombre de método. El tipo de devolución no importa, de hecho, no puede declarar dos métodos que solo difieran de los tipos de devolución. Por el contrario, puede declarar métodos cuyo único nombre sea diferente. Acabo de editar tu pregunta para solucionar esto y algunas otras cosas. -

12 Respuestas

Puede usar el delegado Func en .net 3.5 como parámetro en su método RunTheMethod. El delegado de Func le permite especificar un método que toma varios parámetros de un tipo específico y devuelve un solo 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);
    }
}

Respondido el 17 de enero de 10 a las 21:01

¿Cómo cambiaría la llamada a Func si el método tiene como firma de retorno vacío y sin parámetros? Parece que no puedo hacer que la sintaxis funcione. - user31673

@unknown: En ese caso sería Action en lugar de Func<string, int>. - jon skeet

pero ahora, ¿qué pasa si quieres pasar argumentos al método? - john ktejik

@ user396483 Por ejemplo, Action<int,string> corresponde a un método que toma 2 parámetros (int y string) y devuelve void. - serdar

@NoelWidmer usando Func<double,string,int> corresponde a un método que toma 2 parámetros (double y string) y regresando int. El último tipo especificado es el tipo de retorno. Puede utilizar este delegado para hasta 16 parámetros. Si de alguna manera necesita más, escriba a su propio delegado como public delegate TResult Func<in T1, in T2, (as many arguments as you want), in Tn, out TResult>(T1 arg1, T2 arg2, ..., Tn argn);. Por favor corríjame si no entendí bien. - serdar

Necesitas usar un delegar. En este caso, todos sus métodos toman un string parámetro y devolver un int - esto se representa más simplemente por el Func<string, int> delegar1. Entonces, su código puede volverse 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 desde un expresión lambda, por lo que podría invocar su método de esta manera:

RunTheMethod(x => x.Length);

Eso creará una 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 RunTheMethod método.

Puede usar delegados para suscripciones a eventos, ejecución asincrónica, devoluciones de llamada, todo tipo de cosas. Vale la pena leer sobre ellos, especialmente si desea utilizar LINQ. Yo tengo un artículo cual es cuales son las que reflejan sobre las diferencias entre los delegados y los eventos, pero puede resultarle útil de todos modos.


1 Esto solo se basa en el genérico Func<T, TResult> tipo de delegado en el marco; fácilmente podría declarar el suyo:

public delegate int MyDelegateType(string value)

y luego hacer que el parámetro sea de tipo MyDelegateType preferiblemente.

Respondido el 26 de Septiembre de 16 a las 06:09

+1 Esta es realmente una respuesta increíble para recitar en dos minutos. - David Hall

Si bien puede pasar la función usando delegados, un enfoque OO más tradicional sería usar el patrón de estrategia. - Paul

@Paolo: Los delegados son solo una implementación muy conveniente del patrón de estrategia donde la estrategia en cuestión solo requiere un método único. No es como si esto fuera en contra el patrón de estrategia, pero es mucho más conveniente que implementar el patrón usando interfaces. - jon skeet

¿Siguen siendo útiles los delegados "clásicos" (conocidos por .NET 1/2) o están completamente obsoletos debido a Func / Action? Además, ¿no falta una palabra clave delegada en su ejemplo? public **delegate** int MyDelegateType(string value)? - M4N

@JonSkeet: Primero, fantástico artículo. Sugerencia humilde para una edición: cuando leí la parte donde traduces el lambda en una función anónima, y ​​vi esto: private static int <> _ HiddenMethod _ <> (string x) {...} Estaba bastante confundido por un minuto, porque <> se usa para genéricos, por supuesto. Pasé unos minutos pegando esto en C # para ver si me faltaba algo, y luego me di cuenta de que probablemente solo estabas marcando la parte dinámica. Cambiar esto podría suavizar eso para otros. Aprendí mucho de esto, ¡gracias! - Brian Mackay

Del ejemplo de OP:

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

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

¡Puedes probar Action Delegate! Y luego llama a tu método usando

 public bool RunTheMethod(Action myMethodName)
 {
      myMethodName();   // note: the return value got discarded
      return true;
 }

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

Or

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

Entonces simplemente llame al método

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

Console.WriteLine(InvokeMethod(new Func<string, int>(Method2), "MyString2"));

Respondido 25 Feb 19, 21:02

Gracias, esto me llevó a donde quería ir, ya que quería un método "RunTheMethod" más genérico que permitiera múltiples parámetros. Por cierto tu primero InvokeMethod La llamada lambda debe ser RunTheMethod en lugar de - John

Al igual que John, esto realmente me ayudó a tener un método genérico de movimiento. ¡Gracias! - andan5533

Me alegraste el día;) Realmente simple de usar y mucho más flexible que la respuesta seleccionada en mi opinión. - Sidewinder94

¿Hay alguna manera de expandir RunTheMethod (() => Method1 ("MyString1")); recuperar un valor de retorno? ¿Idealmente un genérico? - arrendajo

si desea pasar parámetros, tenga en cuenta esto: stackoverflow.com/a/5414539/2736039 - Ultimo_m

public static T Runner<T>(Func<T> funcToRun)
{
    //Do stuff before running function as normal
    return funcToRun();
}

Uso:

var ReturnValue = Runner(() => GetUser(99));

Respondido 14 ago 14, 03:08

Es muy útil. De esta forma, se pueden utilizar uno o varios parámetros. Supongo que la respuesta más actualizada es esta. - bafsar

Me gustaría agregar una cosa sobre esta implementación. Si el método que va a aprobar tiene un tipo de retorno de vacío, entonces no puede usar esta solución. - Imanes Volkovs

@ImantsVolkovs Creo que podrías modificar esto para usar una Acción en lugar de una Func, y cambiar la firma a void. Sin embargo, no estoy 100% seguro. - Shockwave

¿Hay alguna forma de pasar los parámetros a la función que se llama? - Jimmy

Para dar una respuesta clara y completa, empezaré desde el principio antes de proponer tres posibles soluciones.


Una breve introduccion

Todos los idiomas que se ejecutan sobre CLR (Tiempo de ejecución de lenguaje común), como C #, F # y Visual Basic, funcionan en una máquina virtual que ejecuta un código de nivel superior que el código de máquina, en el que se compilan los lenguajes nativos como C y C ++. De ello se deduce que los métodos no son subrutinas de ensamblaje, ni son valores, a diferencia de JavaScript y de la mayoría de los lenguajes funcionales; más bien, son definiciones que CLR reconoce. Por lo tanto, no se puede pensar en pasar un método como parámetro, porque los métodos no producen ningún valor por sí mismos, ya que no son expresiones sino declaraciones, que se almacenan en los ensamblados generados. En este punto, se enfrentará a los delegados.


¿Qué es un delegado?

Un delegado representa un identificador de un método (el término encargarse de es preferible a puntero ya que depende de la implementación). Como dije anteriormente, un método no es un valor, por lo tanto, hay una clase especial en los lenguajes CLR, a saber Delegate, que resume cualquier método.

Mira el siguiente ejemplo:

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

Tres soluciones diferentes, el mismo concepto subyacente:

  • El tipo: forma insegura
    Usando el patrón de velas del Delegate clase especial directamente de la misma manera que en el ejemplo anterior. El inconveniente aquí es que su código no es seguro para los tipos, lo que permite que los argumentos se pasen dinámicamente, sin restricciones.

  • La forma personalizada
    Además de la Delegate clase especial, el concepto de delegados se extiende a delegados personalizados, que son declaraciones de métodos precedidos por el delegate palabra clave. Se verifican de la misma manera que las declaraciones de métodos, lo que conduce a un código perfectamente seguro.

    He aquí un 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");
    }
    
  • El camino de la biblioteca estándar
    Alternativamente, puede usar un delegado que sea parte de .NET Standard:

    • Action envuelve un sin parámetros void método.
    • Action<T1> envuelve un void método con un parámetro de tipo T1.
    • Action<T1, T2> envuelve un void método con dos parámetros de tipos T1 y T2, respectivamente.
    • Etcétera...
    • Func<TR> concluye una función sin parámetros con TR tipo de retorno.
    • Func<T1, TR> concluye una función con TR tipo de retorno y con un parámetro de tipo T1.
    • Func<T1, T2, TR> concluye una función con TR tipo de retorno y con dos parámetros de tipos T1 y T2, respectivamente.
    • Etcétera...

    Sin embargo, tenga en cuenta que al usar delegados predefinidos como estos, los nombres de los parámetros no describen lo que tienen que pasar, ni el nombre del delegado es significativo en lo que se supone que debe hacer. Por lo tanto, tenga cuidado sobre cuándo usar estos delegados y absténgase de usarlos en contextos donde su propósito no sea absolutamente evidente.

La última solución es la que publica la mayoría de la gente. Todavía lo menciono en mi respuesta en aras de la integridad.

Respondido el 31 de diciembre de 20 a las 21:12

¿No debería ser el tipo de retorno de un Func? ser el ultimo? Func<T1,T2,TR> - sanmcp

Deberías usar un Func<string, int> delegado, que representa una función que toma un string argumento y devolviendo un int valor:

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

Luego invocalo de esta manera:

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

Respondido 23 ago 20, 15:08

Esto no se compilará. La Test el método debe ser return RunTheMethod(Method1); - pswg

Si desea tener la capacidad de cambiar el método que 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.

Respondido el 17 de enero de 10 a las 21:01

Debería publicar algo por donde comenzar, en lugar de simplemente vincularlo a otro lugar. Los enlaces se pueden romper, mientras que las respuestas de StackOverflow deben incluir la información necesaria. Al menos debe citar algo del enlace que publicó. Por favor refiérase a stackoverflow.com/help/how-to-answer para más información. - david cannizzo

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 marco impulsado por complementos 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 Menu objeto porque el marco puede implementarse en otras plataformas que no tienen Menu Objetos de interfaz de usuario. Agregar información general sobre el menú es bastante fácil, pero permitir que el desarrollador del complemento tenga la libertad suficiente para crear la devolución de llamada para cuando se hace clic en el menú resultó ser una molestia. ¡Hasta que me di cuenta de que estaba tratando de reinventar la rueda y los menús normales llaman y activan 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, heredados de una base si es necesario, y simplemente agregue un controlador de eventos a cada uno.

respondido 29 mar '15, 14:03

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

Supongamos que tiene Padre página 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 secundario.

Aquí necesitas crear un delegado.

Parent.cs // declaración de delegados public delegate void FillName (String FirstName);

Ahora cree una función que llene su cuadro de texto y la función debería asignar a los delegados

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

Ahora, al hacer clic en el botón, debe abrir una ventana emergente infantil.

  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, debe crear el parámetro de 'tipo de delegado' 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;
    }

contestado el 25 de mayo de 16 a las 10:05

Editado, pero la calidad de esta respuesta aún podría mejorarse. - bistecdesbordamiento

Si desea pasar Method como parámetro, 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");
}

Respondido el 06 de Septiembre de 19 a las 18:09

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. luego usa ambos con el método Invoke.

params Objeto [] parámetros

Respondido el 17 de enero de 10 a las 21:01

Tenga en cuenta que el nombre del método no está en una cadena; en realidad, es como un grupo de métodos. Los delegados son la mejor respuesta aquí, no la reflexión. - jon skeet

@Lette: en la invocación del método, la expresión utilizada como argumento es una grupo de métodos; es el nombre de un método que se conoce en tiempo de compilación, y el compilador puede convertirlo en un delegado. Esto es muy diferente de la situación en la que el nombre solo se conoce en el momento de la ejecución. - jon skeet

class PersonDB
{
  string[] list = { "John", "Sam", "Dave" };
  public void Process(ProcessPersonDelegate f)
  {
    foreach(string s in list) f(s);
  }
}

La segunda clase es Cliente, que utilizará la clase de almacenamiento. Tiene un método Main que crea una instancia de PersonDB y llama al método Process de ese objeto con un método definido en la clase Client.

class Client
{
  static void Main()
  {
    PersonDB p = new PersonDB();
    p.Process(PrintName);
  }
  static void PrintName(string name)
  {
    System.Console.WriteLine(name);
  }
}

Respondido el 10 de Septiembre de 19 a las 21:09

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.