Передать метод в качестве параметра, используя C # - PullRequest
614 голосов
/ 18 января 2010

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

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, так как мне нужно определить параметр.

Ответы [ 10 ]

763 голосов
/ 18 января 2010

Вы можете использовать делегат 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);
    }
}
339 голосов
/ 18 января 2010

Вам необходимо использовать делегат . В этом случае все ваши методы принимают параметр 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;
}

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

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. У меня есть статья , которая в основном о различиях между делегатами и событиями, но вы все равно можете найти ее полезной.


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

public delegate int MyDelegateType(string value)

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

101 голосов
/ 14 октября 2011

Из примера OP:

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

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

Вы можете попробовать Action Delegate! А затем вызовите ваш метод, используя

 public bool RunTheMethod(Action myMethodName)
 {
      myMethodName();   // note: the return value got discarded
      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"));
29 голосов
/ 14 августа 2014
public static T Runner<T>(Func<T> funcToRun)
{
    //Do stuff before running function as normal
    return funcToRun();
}

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

var ReturnValue = Runner(() => GetUser(99));
11 голосов
/ 18 января 2010

Вы должны использовать делегат 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);
}
6 голосов
/ 15 мая 2018

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


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

Все языки CLR ( Common Language Runtime ) (такие как C # и 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, и ведут себя как обычный метод. Они так проверены, и вы получите код " perfect ".

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

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<T1, TR> упаковывает функцию с TR возвращаемым типом и одним аргументом.
  • Func<T1, T2, TR> оборачивает функцию с TR возвращаемым типом и двумя аргументами.
  • И так далее ...

(Это последнее решение, которое публикуют многие люди.)

6 голосов
/ 18 января 2010

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

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

2 голосов
/ 29 марта 2015

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

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

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

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

1 голос
/ 24 сентября 2013

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

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

Здесь необходимо создать делегат.

Parent.cs // объявление делегатов публичный делегат 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 необходимо создать параметр «тип делегата» родительского // page

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 голосов
/ 18 января 2010

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

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

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

params Object [] параметры

...