关于.net:使用C#将方法作为参数传递

Pass Method as Parameter using C#

我有几个方法都具有相同的签名(参数和返回值),但是不同的名称和方法的内部结构是不同的。我想将要运行的方法的名称传递给另一个将调用传入方法的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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代码,因为我需要定义参数。


可以使用.NET 3.5中的func委托作为runthemethod方法中的参数。func委托允许您指定一个方法,该方法接受特定类型的多个参数,并返回特定类型的单个参数。下面是一个应该起作用的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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);
    }
}


您需要使用代理。在这种情况下,您的所有方法都使用一个string参数并返回一个int—这最简单地由Funcdelegate1表示。因此,只要进行如下简单的更改,您的代码就可以变得正确:

1
2
3
4
5
6
7
public bool RunTheMethod(Func<string, int> myMethodName)
{
    // ... do stuff
    int i = myMethodName("My String");
    // ... do more stuff
    return true;
}

诚然,代表们比这更有权力。例如,使用C可以从lambda表达式创建委托,因此可以这样调用方法:

1
RunTheMethod(x => x.Length);

这将创建一个匿名函数,如下所示:

1
2
3
4
5
6
// 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委托类型;您可以轻松地声明自己的:

1
public delegate int MyDelegateType(string value)

然后将参数改为MyDelegateType类型。


从OP的例子来看:

1
2
3
4
5
6
7
8
9
 public static int Method1(string mystring)
 {
      return 1;
 }

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

你可以试试动作代表!然后用

1
2
3
4
5
6
7
 public bool RunTheMethod(Action myMethodName)
 {
      myMethodName();   // note: the return value got discarded
      return true;
 }

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

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

然后简单地调用方法

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

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


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

用途:

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


您应该使用一个Func委托,它表示一个以string为参数并返回int的函数:

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

然后使用它:

1
2
3
public bool Test() {
    return RunTheMethod(Method1);
}


为了分享一个尽可能完整的解决方案,我将以介绍三种不同的方式结束,但现在我将从最基本的原则开始。

简介

所有的CLR(公共语言运行时)语言(如C.*和Visual Basic)都在一个称为CLI(公共语言解释器)的VM下工作,它在比C语言和C++语言(直接编译成机器代码)的更高级别上运行代码。由此可知,方法不是任何类型的编译块,但它们只是结构化元素,CLR可以识别并使用这些元素来拉出它们的主体,并将其重新连接到机器代码的内联指令中。因此,您不能认为将方法作为参数传递,因为方法本身不产生任何值:它不是有效的表达式!这样,您就将绊倒代理的概念。

代表是什么?

委托表示指向方法的指针。因为(正如我上面所说)一个方法不是一个值,所以在CLR语言中有一个特殊的类:Delegate。该类包装任何方法,您可以将任何方法隐式强制转换为该方法。

请看下面的用法示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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关键字前面的方法声明,它们的行为类似于普通方法。它们被检查过了,你会得到一个"完美"的代码。

请看以下示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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用一个参数包装void
  • Action用两个参数包装void
  • 等等…
  • FuncTR返回类型和无参数包装函数。
  • FuncTR返回类型和一个参数包装函数。
  • FuncTR返回类型和两个参数包装函数。
  • 等等…

(后一种解决方案是许多人发布。)


如果您希望能够更改在运行时调用的方法,我建议使用委托:http://www.codeproject.com/kb/cs/delegates_step1.aspx

它将允许您创建一个对象来存储要调用的方法,并且您可以在需要时将其传递给其他方法。


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

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

所以这个简单的解决方案,在你意识到它之后,一直闪过我的视线,直到现在。

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


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

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

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

帕伦茨//委托声明公共委托void fillname(string firstname);

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

1
2
3
4
5
//parameters
public void Getname(String ThisName)
{
     txtname.Text=ThisName;
}

现在单击按钮,您需要打开一个子弹出窗口。

1
2
3
4
5
6
7
  private void button1_Click(object sender, RoutedEventArgs e)
  {
        ChildPopUp p = new ChildPopUp (Getname) //pass function name in its constructor

         p.Show();

    }

在ChildPopup构造函数中,需要创建父//页的"委托类型"参数

童子军

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    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_常见问题解答:_How_Call_a_method_using_a_name_string

与帕拉姆斯:http://www.daniweb.com/forums/thread98148.html#

基本上,您将传入一个对象数组和方法名。然后将这两种方法都用于invoke方法。

参数对象[]参数