Передать метод в качестве параметра, используя C #

533

У меня есть несколько методов с одной и той же сигнатурой (параметры и возвращаемые значения), но разные имена и внутренности методов разные. Я хочу передать имя метода для запуска к другому методу, который будет вызывать переданный метод.

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

Этот код не работает, но это то, что я пытаюсь сделать. Я не понимаю, как написать код RunTheMethod, так как мне нужно определить параметр.

  • 9
    Почему бы вам не передать делегат вместо имени метода?
Теги:
delegates
methods

10 ответов

699
Лучший ответ

Вы можете использовать делегат Func в .net 3.5 в качестве параметра в вашем методе RunTheMethod. Делегат Func позволяет указать метод, который принимает несколько параметров определенного типа и возвращает один аргумент определенного типа. Вот пример, который должен работать:

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);
    }
}
  • 37
    Как изменится вызов Func, если у метода есть сигнатура возврата void и нет параметров? Я не могу заставить синтаксис работать.
  • 177
    @unknown: в этом случае это будет Action вместо Func<string, int> .
Показать ещё 9 комментариев
311

Вам нужно использовать делегат. В этом случае все ваши методы принимают параметр string и возвращают int - это наиболее просто представлено делегатом Func<string, int> 1. Таким образом, ваш код может стать правильным с таким простым изменением, как это:

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

Делегаты обладают гораздо большей властью, чем это, по общему признанию. Например, с С# вы можете создать делегат из выражения лямбда, чтобы вы могли вызвать ваш метод таким образом:

RunTheMethod(x => x.Length);

Это создаст анонимную функцию:

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

а затем передайте этот делегат методу RunTheMethod.

Вы можете использовать делегаты для подписки на события, асинхронное выполнение, обратные вызовы - всевозможные вещи. Это стоит прочитать на них, особенно если вы хотите использовать LINQ. У меня есть article, который в основном касается различий между делегатами и событиями, но в любом случае вы можете найти его полезным.


1 Это основано только на общий тип делегата Func<T, TResult> в структуре; вы можете легко заявить о себе:

public delegate int MyDelegateType(string value)

а затем сделайте вместо этого параметр MyDelegateType.

  • 49
    +1 Это действительно потрясающий ответ, который можно услышать за две минуты.
  • 3
    В то время как вы можете передать функцию с помощью делегатов, более традиционный ОО подход будет использовать шаблон стратегии.
Показать ещё 15 комментариев
87

Вы также можете попробовать 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;
 }

И затем вызовите свой метод, используя

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

Или

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

Затем просто вызовите метод

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

Console.WriteLine(InvokeMethod(new Func<string, int>(Method2), "MyString2"));
  • 3
    Спасибо, это привело меня туда, куда я хотел пойти, так как я хотел более общий метод "RunTheMethod", который позволял бы использовать несколько параметров. Btw ваш первый InvokeMethod лямбда - вызов должен быть RunTheMethod вместо
  • 1
    Как и Джон, это действительно помогло мне использовать универсальный метод перемещения. Спасибо!
Показать ещё 3 комментария
25
public static T Runner<T>(Func<T> funcToRun)
{
    //Do stuff before running function as normal
    return funcToRun();
}

Использование:

var ReturnValue = Runner(() => GetUser(99));
  • 6
    Это очень удобно. Таким способом можно использовать один или несколько параметров. Я думаю, самый обновленный ответ - это.
  • 0
    Я хотел бы добавить одну вещь об этой реализации. Если метод, который вы собираетесь передать, имеет тип возврата void, вы не можете использовать это решение.
Показать ещё 2 комментария
10

Вы должны использовать делегат Func<string, int>, представляющий функцию, принимающую аргумент string в качестве аргумента и возвращающий int:

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

Затем используйте его:

public bool Test() {
    return RunTheMethod(Method1);
}
  • 3
    Это не скомпилируется. Метод Test должен быть return RunTheMethod(Method1);
6

Если вы хотите изменить метод, который вызывается во время выполнения, я бы рекомендовал использовать делегат: http://www.codeproject.com/KB/cs/delegates_step1.aspx

Это позволит вам создать объект для хранения метода для вызова, и вы можете передать его другим методам, когда это необходимо.

2

Чтобы поделиться как можно более полным решением, я собираюсь привести три разных способа сделать, но теперь я собираюсь начать с самого элементарного принципа.


Краткое введение

Все языки CLR (Common Language Runtime) (например, С# и Visual Basic) работают под виртуальной машиной, называемой CLI (Common Language Interpreter), которая запускает код на более высоком уровне, чем родные языки, такие как C и C++ (которые непосредственно компилируются на машину код). Из этого следует, что методы не являются каким-либо скомпилированным блоком, но они представляют собой только структурированные элементы, которые CLR распознают и используют для вытягивания своего тела и привязывают его к встроенным инструкциям машинного кода. Таким образом, вы не можете думать, чтобы передать метод в качестве параметра, потому что метод не производит никакого значения сам по себе: это не допустимое выражение! Таким образом, вы собираетесь споткнуться о концепции делегата.


Какой делегат?

Делегат представляет собой указатель на метод. Из-за (как я уже сказал выше) метод не является значением, там есть специальный класс на языках CLR: Delegate. Этот класс обертывает любой метод, и вы можете косвенно использовать любой метод для этого.

Посмотрите следующий пример использования:

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

Три способа:

  • Способ 1
    Используйте специальный класс Delegate непосредственно в качестве примера выше. Проблема этого решения заключается в том, что ваш код будет отключен, поскольку вы динамически передаете свои аргументы, не ограничивая их типами в объявлении метода.

  • Путь 2/3 Помимо Delegate специального класса, концепция делегатов распространяется на пользовательские делегат, которые являются выражением методов предваряются по delegate ключевого слову, и они ведут себя как обычный метод. Они так проверены, и вы подходите к "идеальному" коду.

Посмотрите на следующий пример:

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

Второй вариант, чтобы не писать собственный пользовательский делегат, использует один из них, объявленный в системных библиотеках:

  • Action завершает void без аргументов.
  • Action<T1> обертывает void одним аргументом.
  • Action<T1, T2> обертывает void двумя аргументами.
  • И так далее...
  • Func<TR> обертывает функцию с типом возврата TR и без аргументов.
  • Func<TR, T1> обертывает функцию с типом возврата TR и одним аргументом.
  • Func<TR, T1, T2> обертывает функцию с типом возврата TR и двумя аргументами.
  • И так далее...

(Это последнее решение состоит в том, что многие люди разместили.)

2

Хотя принятый ответ абсолютно правильный, я хотел бы предоставить дополнительный метод.

Я оказался здесь после того, как сделал свой собственный поиск решения аналогичного вопроса. Я создаю фреймворк, подключенный к плагину, и, как часть его, я хотел, чтобы люди могли добавлять пункты меню в меню приложений в общий список, не подвергая действительный объект Menu, потому что инфраструктура может развертываться на других платформах, t имеют объекты Menu UI. Добавление общей информации о меню достаточно просто, но разрешение разработчику плагина достаточной свободы для создания обратного вызова при нажатии на меню оказалось болью. Пока меня не стало, что я пытался заново изобрести колесо и обычное меню вызова и вызвать обратный вызов из событий!

Итак, решение, столь же простое, как кажется, когда вы это осознаете, ускользало от меня до сих пор.

Просто создайте отдельные классы для каждого из ваших текущих методов, унаследованных от базы, если нужно, и просто добавьте обработчик событий к каждому.

1

Вот пример, который поможет вам лучше понять, как передать функцию в качестве параметра.

Предположим, что у вас есть родительская страница, и вы хотите открыть всплывающее окно для ребенка. На родительской странице есть текстовое поле, которое должно быть заполнено на основе всплывающего текстового поля.

Здесь вам нужно создать делегат.

Parent.cs      // декларация делегатов      public delegate void FillName (String FirstName);

Теперь создайте функцию, которая заполнит ваше текстовое поле, а функция должна отображать делегаты

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

Теперь при нажатии кнопки вам нужно открыть всплывающее окно Child.

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

         p.Show();

    }

В конструкторе ChildPopUp вам необходимо создать параметр "тип делегирования" родительской//страницы

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;
    }
  • 0
    Отредактировано, но качество этого ответа еще можно улучшить.
0

Вот пример без параметра: http://en.csharp-online.net/CSharp_FAQ:_How_call_a_method_using_a_name_string

с параметрами: http://www.daniweb.com/forums/thread98148.html#

вы в основном передаете массив объектов вместе с именем метода. вы затем используете оба метода Invoke.

params Объект [] параметры

  • 0
    Обратите внимание, что имя метода не в строке - это фактически группа методов. Делегаты здесь лучший ответ, а не рефлексия.
  • 0
    я думаю, я упустил суть
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню