使用C#将方法作为参数传递

我有几个方法都具有相同的参数类型和返回值,但名称和块不同。我想将要运行的方法的名称传递给将调用传递方法的另一个方法。

public int Method1(string){// Do somethingreturn myInt;}
public int Method2(string){// Do something differentreturn myInt;}
public bool RunTheMethod([Method Name passed in here] myMethodName){// Do stuffint i = myMethodName("My String");// Do more stuffreturn true;}
public bool Test(){return RunTheMethod(Method1);}

这段代码不起作用,但这是我正在尝试做的。我不明白的是如何编写RunTheWay代码,因为我需要定义参数。

893667 次浏览

您需要使用代表。在这种情况下,您的所有方法都接受string参数并返回int-这最简单地由Func<string, int>委托1表示。因此,您的代码可以通过如此简单的更改变得正确:

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

不可否认,委托比这更强大。例如,使用C#,您可以从lambda表达式创建一个委托,因此您可以这样调用您的方法:

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类型。

您应该使用Func<string, int>委托,它表示接受string参数并返回int值的函数:

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

然后这样调用它:

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

这是一个没有参数的例子: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[]参数

您可以使用. net 3.5中的Func委托作为RunTheWay方法中的参数。Func委托允许您指定一个方法,该方法接受许多特定类型的参数并返回特定类型的单个参数。这是一个应该有效的示例:

public class Class1{public int Method1(string input){//... do somethingreturn 0;}
public int Method2(string input){//... do something differentreturn 1;}
public bool RunTheMethod(Func<string, int> myMethodName){//... do stuffint i = myMethodName("My String");//... do more stuffreturn true;}
public bool Test(){return RunTheMethod(Method1);}}

从OP的例子:

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

您可以尝试操作委托!然后使用以下命令调用您的方法

 public bool RunTheMethod(Action myMethodName){myMethodName();   // note: the return value got discardedreturn 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"));

下面是一个示例,可以帮助您更好地理解如何将函数作为参数传递。

假设您有家长页面并且您想打开一个子弹出窗口。在父页面中有一个文本框,应该根据子弹出文本框填充。

在这里,您需要创建一个委托。

Parent.cs//代表声明公共委托无效FillName(String FirstName);

现在创建一个函数来填充你的文本框,函数应该映射委托

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

现在点击按钮,您需要打开一个儿童弹出窗口。

  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 herethis.DialogResult = true;}

解决方案涉及代表,用于存储要调用的方法。定义一个以委托为参数的方法,

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

然后在调用站点上传递委托:

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

虽然接受的答案是绝对正确的,但我想提供另一种方法。

在我自己寻找类似问题的解决方案之后,我最终来到了这里。我正在构建一个插件驱动的框架,作为其中的一部分,我希望人们能够将菜单项添加到应用程序菜单到通用列表中,而无需暴露实际的Menu对象,因为该框架可能部署在没有Menu UI对象的其他平台上。添加有关菜单的一般信息很容易,但是允许插件开发人员有足够的自由来创建单击菜单时的回调被证明是一种痛苦。直到我意识到我正在尝试重新发明轮子和普通菜单调用并从事件触发回调!

所以解决方案,听起来很简单,一旦你意识到这一点,直到现在我才意识到。

只需为当前的每个方法创建单独的类,如果必须,则从基继承,并为每个方法添加一个事件处理程序。

为了提供一个清晰而完整的答案,我将从头开始,然后提出三种可能的解决方案。


一个简短的介绍

所有运行在CLR(公共语言运行库)之上的语言,如C#、F#和Visual Basic,都在运行比机器代码更高级别代码的虚拟机下工作。因此,与JavaScript和大多数函数式语言不同,方法不是汇编子程序,也不是值;相反,它们是CLR识别的符号。因此,你不能认为将方法作为参数传递,因为方法本身不产生任何值,因为它们不是表达式而是语句,存储在生成的程序集中。在这一点上,你将面对委托。


什么是代表?

委托表示方法的句柄(术语处理应该优先于指针,因为后者是实现细节)。由于方法不是值,因此在. NET中必须有一个特殊的类,即Delegate,它包装了任何方法。它的特殊之处在于,像极少数类一样,它需要由CLR本身实现,不能由自己实现。

看下面的例子:

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

三种不同的解决方案,相同的基本概念

  • 类型不安全的方式

    直接使用Delegate特殊类与上面的示例相同。这里的缺点是您的代码类型不安全,允许参数动态传递,没有约束。

  • 自定义方式

    除了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");}
  • 标准库的方式

    或者,您可以使用作为. NET Standard一部分的委托:

    • Action包装了一个无参数的void方法。
    • Action<T1>T1类型的一个参数包装void方法。
    • Action<T1, T2>用两个类型分别为T1T2的参数包装void方法。
    • 等等…
    • Func<TR>TR返回类型包装一个无参数函数。
    • Func<T1, TR>TR返回类型和T1类型的一个参数包装一个函数。
    • Func<T1, T2, TR>包装一个返回类型为TR的函数,并分别使用类型为T1T2的两个参数。
    • 等等…

    但是,请记住,通过使用像这样的预定义委托,参数名称不会描述它们必须传入的内容,委托名称对它应该做什么也没有意义。因此,使用这些委托时要小心,不会影响代码自描述的好处,并避免在其目的不是绝对不言自明的上下文中使用它们。

后一种解决方案是大多数人发布的。为了完整起见,我也在我的回答中提到了它。

如果您想将方法作为参数传递,请使用:

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 it as a parameter");}
class PersonDB{string[] list = { "John", "Sam", "Dave" };public void Process(ProcessPersonDelegate f){foreach(string s in list) f(s);}}

第二个类是Client,它将使用储存类别。它有一个Main方法来创建一个个性化数据库的实例,并使用在Client类中定义的方法调用该对象的Process方法。

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

我不知道谁可能需要这个,但是如果你不确定如何发送一个带有委托的lambda,当使用委托的函数不需要在那里插入任何参数时,你只需要返回值。

你也可以这样做:

public int DoStuff(string stuff){Console.WriteLine(stuff);}
public static bool MethodWithDelegate(Func<int> delegate){///do stuffint i = delegate();return i!=0;}
public static void Main(String[] args){var answer = MethodWithDelegate(()=> DoStuff("On This random string that the MethodWithDelegate doesn't know about."));}

如果传递的方法需要接受一个参数并返回一个值,那么Func是最好的方法。这是一个例子。

public int Method1(string){// Do somethingreturn 6;}
public int Method2(string){// Do something differentreturn 5;}
public bool RunTheMethod(Func<string, int> myMethodName){// Do stuffint i = myMethodName("My String");Console.WriteLine(i); // This is just in place of the "Do more stuff"return true;}
public bool Test(){return RunTheMethod(Method1);}

阅读文档这里

但是,如果您作为参数传递的方法不返回任何内容,您也可以使用Action。它最多支持传递方法的16个参数。这是一个示例。

public int MethodToBeCalled(string name, int age){Console.WriteLine(name + "'s age is" + age);}
public bool RunTheMethod(Action<string, int> myMethodName){// Do stuffmyMethodName("bob", 32); // Expected output: "bob's age is 32"return true;}
public bool Test(){return RunTheMethod(MethodToBeCalled);}

留档这里