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

Pass Method as Parameter using C#

提问人:user31673 提问时间:1/18/2010 最后编辑:Davide Cannizzouser31673 更新时间:1/12/2023 访问量:964749

问:

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

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

C# .NET 方法 委托

评论

14赞 Mark Byers 1/18/2010
为什么不传递委托而不是方法的名称?
0赞 Davide Cannizzo 8/23/2020
问题声明方法签名是关于参数和返回值的,当它实际上包含参数类型和方法名称时。返回类型无关紧要,实际上,您不能声明两个仅与返回类型不同的方法。相反,您可以声明其唯一名称不同的方法。我刚刚编辑了您的问题以解决此问题和其他一些事情。

答:

428赞 Jon Skeet 1/18/2010 #1

您需要使用委托。在本例中,所有方法都采用一个参数并返回一个 - 这最简单地由委托1 表示。因此,您的代码可以通过以下简单的更改变得正确:stringintFunc<string, int>

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。我有一篇文章主要是关于代表和事件之间的差异,但无论如何你可能会发现它很有用。


1 这只是基于框架中的泛型 Func<T、TResult> 委托类型;您可以轻松地声明自己的:

public delegate int MyDelegateType(string value)

然后将参数改为类型。MyDelegateType

评论

81赞 David Hall 1/18/2010
+1 这真的是一个惊人的答案,可以在两分钟内嘎嘎作响。
3赞 Paolo 1/18/2010
虽然您可以使用委托传递函数,但更传统的 OO 方法是使用策略模式。
25赞 Jon Skeet 1/18/2010
@Paolo:委托只是策略模式的一个非常方便的实现,其中所讨论的策略只需要一种方法。这并不是说这与策略模式背道而驰 - 但它比使用接口实现模式要方便得多。
5赞 M4N 1/18/2010
“经典”委托(从 .NET 1/2 中得知)是否仍然有用,或者它们是否由于 Func/Action 而完全过时?另外,您的示例中是否缺少委托关键字?public **delegate** int MyDelegateType(string value)
4赞 Brian MacKay 8/12/2016
@JonSkeet:首先,精彩的文章。拙劣的编辑建议:当我阅读您将 lambda 转换为匿名函数的部分时,并看到以下内容: private static int <>_HiddenMethod_<>(string x) { ... }我有一分钟很困惑,因为<>当然是用于泛型的。我花了几分钟时间在 C# 中粘贴它,看看我是否遗漏了什么,然后意识到您可能只是在标记动态部分。改变这一点可能会让其他人平滑。从中学到了很多东西,谢谢!
16赞 Bruno Reis 1/18/2010 #2

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

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

然后这样调用它:

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

评论

4赞 p.s.w.g 8/24/2013
这不会编译。方法应为Testreturn RunTheMethod(Method1);
0赞 Jeremy Samuel 1/18/2010 #3

下面是一个不带参数的示例: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[] 参数

评论

0赞 Jon Skeet 1/18/2010
请注意,方法的名称不在字符串中 - 它实际上是一个方法组。代表是这里最好的答案,而不是反思。
0赞 Jon Skeet 1/18/2010
@Lette:在方法调用中,用作参数的表达式是方法组;它是在编译时已知的方法的名称,编译器可以将其转换为委托。这与仅在执行时知道名称的情况有很大不同。
0赞 fbiazi 5/21/2022
不要只是粘贴链接。链接必须用作参考,也不得省略。答案的第一个环节断了,如果第二个环节断了,你的答案就变得没用了。让它对实际内容有用,解决方案应该在这里,至少是它的核心。“params Object[] parameters”是不够的。
1043赞 Egil Hansen 1/18/2010 #4

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

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

评论

73赞 user31673 1/18/2010
如果 Method 具有返回 void 且没有参数的签名,Func 调用将如何更改?我似乎无法让语法正常工作。
264赞 Jon Skeet 1/18/2010
@unknown:在这种情况下,它将代替 .ActionFunc<string, int>
15赞 john k 2/24/2014
但是现在,如果您想将参数传递给方法怎么办??
55赞 serdar 6/12/2014
@user396483 例如,对应于一个方法,该方法采用 2 个参数(int 和 string)并返回 void。Action<int,string>
32赞 serdar 9/11/2014
@NoelWidmer using 对应于一个方法,该方法接受 2 个参数 ( 和 ) 并返回 .最后指定的类型是返回类型。您最多可以将此委托用于 16 个参数。如果您以某种方式需要更多,请将自己的委托写为 .如果我误解了,请纠正我。Func<double,string,int>doublestringintpublic delegate TResult Func<in T1, in T2, (as many arguments as you want), in Tn, out TResult>(T1 arg1, T2 arg2, ..., Tn argn);
136赞 Zain Ali 10/14/2011 #5

从 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"));

评论

5赞 John 1/28/2012
谢谢,这让我到达了我想去的地方,因为我想要一个更通用的“RunTheMethod”方法,该方法允许多个参数。顺便说一句,您的第一个 lambda 调用应该是InvokeMethodRunTheMethod
1赞 ean5533 11/7/2012
像 John 一样,这确实帮助我拥有了一种移动通用方法。谢谢!
2赞 Irwene 3/27/2014
你让我的一天变得;)使用起来非常简单,比选定的答案IMO灵活得多。
0赞 Jay 7/6/2016
有没有办法扩展RunTheMethod((() => Method1(“MyString1”));检索返回值?理想情况下是通用的?
0赞 Ultimo_m 6/13/2018
如果要传递参数,请注意这一点: stackoverflow.com/a/5414539/2736039
2赞 Shrikant-Divyanet Solution 9/24/2013 #6

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

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

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

父级 .cs 代表声明 公共委托 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 构造函数中,您需要创建父 //page 的 'delegate type' 参数

儿童弹出.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赞 Steak Overflow 5/25/2016
已编辑,但此答案的质量仍有待提高。
39赞 kravits88 8/14/2014 #7

该解决方案涉及委托,这些委托用于存储要调用的方法。定义一个将委托作为参数的方法,

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

然后在呼叫站点上传递委托:

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

评论

7赞 bafsar 1/24/2015
这是非常有用的。通过这种方式,可以使用一个或多个参数。我想,最新的答案是这个。
0赞 Imants Volkovs 5/20/2016
关于这个实现,我想补充一点。如果要传递的方法的返回类型为 void,则不能使用此解决方案。
0赞 Shockwave 3/27/2017
@ImantsVolkovs我相信您可以将其修改为使用 Action 而不是 Func,并将签名更改为 void。虽然不是100%确定。
2赞 Jimmy 8/4/2017
有没有办法将参数传递给被调用的函数?
2赞 Wobbles 3/29/2015 #8

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

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

因此,解决方案,尽管听起来很简单,但直到现在我都没有。

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

67赞 Davide Cannizzo 5/15/2018 #9

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


简介

所有 .NET 语言(如 C#、F# 和 Visual Basic)都运行在公共语言运行时 (CLR) 之上,CLR 是以公共中间语言 (CIL) 运行代码的 VM,其级别比机器代码高得多。因此,与函数式语言和 JavaScript 不同,方法不是 Assembly 子例程,也不是值;相反,它们是 CLR 识别的符号。不是值,它们不能作为参数传递。这就是为什么 .NET 中有一个特殊的工具。也就是说,代表。


什么是委托?

委托表示方法的句柄(术语句柄优先于指针,因为指针是实现细节)。由于方法不是值,因此 .NET 中必须有一个特殊的类,即 ,它包装了任何方法。它的特别之处在于,与极少数类一样,它需要由 CLR 本身实现,而不能简单地用 .NET 语言编写为类。Delegate


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

  • 类型不安全的方式

    直接使用特殊类。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);
    }
    

    这里的缺点是你的代码是类型不安全的,允许动态传递参数,没有约束。

  • 定制方式

    除了特殊类之外,委托的概念还扩展到自定义委托,自定义委托是方法的声明,前面有关键字。它们的类型检查方式与“普通”方法调用相同,从而形成类型安全代码。Delegatedelegate

    例:

    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 的委托:Alternatively, you can stick with a delegate that's part of the .NET Standard:

    • Action总结了一个无参数方法;void
    • Action<T1>使用一个 type 参数包装方法voidT1;
    • Action<T1, T2>用 types 和 , 的两个参数来总结一个方法,voidT1T2
    • 等等;
    • Func<TR>用返回类型包装一个无参数函数;TR
    • Func<T1, TR>使用返回类型和一个 type 参数包装函数TRT1;
    • Func<T1, T2, TR>用返回类型和两个参数分别包装一个函数 和 ;TRT1T2
    • 等等。

    但是,请记住,通过使用此类预定义委托,参数名称不会是自描述的,委托类型的名称对于实例应该执行的操作也没有意义。因此,不要在其目的并非绝对不言而喻的情况下使用它们。

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

评论

2赞 Stack Undefined 5/14/2019
Func<T> 的返回类型不应该是最后一个吗?Func<T1,T2,TR>
2赞 Junaid Pathan 9/7/2019 #10

如果要将 Method 作为参数传递,请使用:

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");
}
0赞 user7396942 9/11/2019 #11
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);
  }
}
0赞 rtgher 4/1/2022 #12

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

因此,您也可以这样做:

public int DoStuff(string stuff)
{
    Console.WriteLine(stuff);
}

public static bool MethodWithDelegate(Func<int> delegate)
{
    ///do stuff
    int 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."));
}
1赞 Caleb Liu 6/20/2022 #13

如果传递的方法需要接受一个参数并返回一个值,则最好是方法。下面是一个示例。Func

public int Method1(string)
{
    // Do something
    return 6;
}

public int Method2(string)
{
    // Do something different
    return 5;
}

public bool RunTheMethod(Func<string, int> myMethodName)
{
    // Do stuff
    int 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);
}

在此处阅读文档

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

public int MethodToBeCalled(string name, int age)
{
    Console.WriteLine(name + "'s age is" + age);
}

public bool RunTheMethod(Action<string, int> myMethodName)
{
    // Do stuff
    myMethodName("bob", 32); // Expected output: "bob's age is 32"
    return true;
}

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

在此处阅读文档