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

时间:2010-01-17 21:01:01

标签: c# .net methods delegates

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

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代码,因为我需要定义参数。

12 个答案:

答案 0 :(得分:746)

您可以使用.net 3.5中的Func委托作为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);
    }
}

答案 1 :(得分:335)

您需要使用委托。在这种情况下,您的所有方法都会使用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#,您可以从 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。我有一个article 主要是关于代理和事件之间的差异,但无论如何你可能会发现它很有用。


1 这只是基于框架中的通用Func<T, TResult>委托类型;你可以轻松宣布自己的:

public delegate int MyDelegateType(string value)

然后将参数改为MyDelegateType类型。

答案 2 :(得分:98)

来自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 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"));

答案 3 :(得分:28)

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

用法:

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

答案 4 :(得分:11)

您应该使用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);
}

答案 5 :(得分:6)

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

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

答案 6 :(得分:6)

为了尽可能完整地分享解决方案,我最终会提出三种不同的做法,但现在我将从最基本的原则开始。

简介

所有CLR(公共语言运行时)语言(例如C#和Visual Basic)在名为CLI(公共语言解释器)的VM下运行,该VM运行代码比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关键字之前的方法声明,它们的行为类似于普通方法。他们经过如此严格的检查,你会来到一个完美的&#34;码。

请看以下示例:

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返回类型和两个参数包装函数。
  • 依旧......

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

答案 7 :(得分:2)

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

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

因此,解决方案,一旦你意识到这一点就听起来很简单,直到现在还没有找到我。

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

答案 8 :(得分:1)

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

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

您需要创建一个委托。

Parent.cs        //代表声明        public delegate void FillName(String FirstName);

现在创建一个填充文本框的函数,函数应该映射代理

//parameters
public 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 here
        this.DialogResult = true;
    }

答案 9 :(得分:1)

如果要将“方法”作为参数传递,请使用:

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

答案 10 :(得分:0)

以下是没有参数的示例: http://en.csharp-online.net/CSharp_FAQ:_How_call_a_method_using_a_name_string

用params: http://www.daniweb.com/forums/thread98148.html#

你基本上传入了一个对象数组以及方法名。然后使用Invoke方法。

params Object []参数

答案 11 :(得分:0)

class PersonDB
{
  string[] list = { "John", "Sam", "Dave" };
  public void Process(ProcessPersonDelegate f)
  {
    foreach(string s in list) f(s);
  }
}

第二个类是Client,它将使用存储类。它具有一个Main方法,该方法创建一个PersonDB实例,并使用Client类中定义的方法调用该对象的Process方法。

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