У меня есть несколько методов с одной и той же сигнатурой (параметры и возвращаемые значения), но разные имена и внутренности методов разные. Я хочу передать имя метода для запуска к другому методу, который будет вызывать переданный метод.
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, так как мне нужно определить параметр.
Вы можете использовать делегат 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);
}
}
Action
вместо Func<string, int>
.
Вам нужно использовать делегат. В этом случае все ваши методы принимают параметр 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
.
Вы также можете попробовать 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"));
InvokeMethod
лямбда - вызов должен быть RunTheMethod
вместо
public static T Runner<T>(Func<T> funcToRun)
{
//Do stuff before running function as normal
return funcToRun();
}
Использование:
var ReturnValue = Runner(() => GetUser(99));
Вы должны использовать делегат 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);
}
Test
должен быть return RunTheMethod(Method1);
Если вы хотите изменить метод, который вызывается во время выполнения, я бы рекомендовал использовать делегат: http://www.codeproject.com/KB/cs/delegates_step1.aspx
Это позволит вам создать объект для хранения метода для вызова, и вы можете передать его другим методам, когда это необходимо.
Чтобы поделиться как можно более полным решением, я собираюсь привести три разных способа сделать, но теперь я собираюсь начать с самого элементарного принципа.
Все языки 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
и двумя аргументами.(Это последнее решение состоит в том, что многие люди разместили.)
Хотя принятый ответ абсолютно правильный, я хотел бы предоставить дополнительный метод.
Я оказался здесь после того, как сделал свой собственный поиск решения аналогичного вопроса.
Я создаю фреймворк, подключенный к плагину, и, как часть его, я хотел, чтобы люди могли добавлять пункты меню в меню приложений в общий список, не подвергая действительный объект Menu
, потому что инфраструктура может развертываться на других платформах, t имеют объекты Menu
UI. Добавление общей информации о меню достаточно просто, но разрешение разработчику плагина достаточной свободы для создания обратного вызова при нажатии на меню оказалось болью. Пока меня не стало, что я пытался заново изобрести колесо и обычное меню вызова и вызвать обратный вызов из событий!
Итак, решение, столь же простое, как кажется, когда вы это осознаете, ускользало от меня до сих пор.
Просто создайте отдельные классы для каждого из ваших текущих методов, унаследованных от базы, если нужно, и просто добавьте обработчик событий к каждому.
Вот пример, который поможет вам лучше понять, как передать функцию в качестве параметра.
Предположим, что у вас есть родительская страница, и вы хотите открыть всплывающее окно для ребенка. На родительской странице есть текстовое поле, которое должно быть заполнено на основе всплывающего текстового поля.
Здесь вам нужно создать делегат.
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;
}
Вот пример без параметра: http://en.csharp-online.net/CSharp_FAQ:_How_call_a_method_using_a_name_string
с параметрами: http://www.daniweb.com/forums/thread98148.html#
вы в основном передаете массив объектов вместе с именем метода. вы затем используете оба метода Invoke.
params Объект [] параметры