ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

为什么不传递委托而不是方法的名称?
问题声称方法签名是关于参数和返回值的,当它真正包含参数类型和方法名称时。返回类型无关紧要,实际上您不能声明两个仅与返回类型不同的方法。相反,您可以声明唯一名称不同的方法。我刚刚编辑了您的问题以解决此问题和其他一些问题。

E
Egil Hansen

您可以使用 .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);
    }
}

如果方法具有返回 void 且没有参数的签名,那么 Func 调用将如何变化?我似乎无法使语法正常工作。
@unknown:在这种情况下,它将是 Action 而不是 Func<string, int>
但是现在如果你想将参数传递给方法怎么办?
@user396483 例如,Action<int,string> 对应于采用 2 个参数(int 和 string)并返回 void 的方法。
@NoelWidmer 使用 Func<double,string,int> 对应于采用 2 个参数(doublestring)并返回 int 的方法。最后指定的类型是返回类型。您可以将此委托用于最多 16 个参数。如果您不知何故需要更多,请将您自己的委托写为 public delegate TResult Func<in T1, in T2, (as many arguments as you want), in Tn, out TResult>(T1 arg1, T2 arg2, ..., Tn argn);。如果我误解了,请纠正我。
J
Jon Skeet

您需要使用委托。在这种情况下,您的所有方法都接受一个 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 类型。


+1 这真的是一个惊人的答案,可以在两分钟内发出嘎嘎声。
虽然您可以使用委托传递函数,但更传统的 OO 方法是使用策略模式。
@Paolo:委托只是策略模式的一种非常方便的实现,其中所讨论的策略只需要一个方法。这并不是违背策略模式——但它比使用接口实现模式要方便得多。
“经典”委托(从 .NET 1/2 中得知)是否仍然有用,还是因为 Func/Action 而完全过时?另外,您的示例 public **delegate** int MyDelegateType(string value) 中是否缺少委托关键字?
@JonSkeet:首先,精彩的文章。对编辑的谦虚建议:当我阅读将 lambda 转换为匿名函数的部分时,看到了这个: private static int <>_HiddenMethod_<>(string x) { ... } 我很困惑一分钟,因为 <> 用于泛型,当然。我花了几分钟将其粘贴到 C# 中,看看我是否遗漏了什么,然后意识到您可能只是在标记动态部分。改变这一点可能会为其他人平息。从中学到了很多,谢谢!
t
themefield

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

谢谢,这让我得到了我想去的地方,因为我想要一个更通用的“RunTheMethod”方法,它允许多个参数。顺便说一句,您的第一个 InvokeMethod lambda 调用应该是 RunTheMethod
像约翰一样,这确实帮助我有了一个通用的移动方法。谢谢!
你让我很开心;) 使用起来真的很简单,而且比 IMO 选择的答案更灵活。
有没有办法扩展 RunTheMethod(() => Method1("MyString1"));检索返回值?理想情况下是通用的?
如果您想传递参数,请注意:stackoverflow.com/a/5414539/2736039
D
Davide Cannizzo

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

简介

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

什么是代表?

委托代表方法的句柄(术语 handle 应优先于 pointer,因为后者将是实现细节)。由于方法不是值,因此 .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); } 静态无效 PrintOnConsole(字符串提示) { Console.WriteLine(提示); } 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 类型的参数包装了一个 void 方法。 Action 分别用两个 T1 和 T2 类型的参数包装了一个 void 方法。等等...... Func 包装了一个带有 TR 返回类型的无参数函数。 Func 包装了一个具有 TR 返回类型和一个 T1 类型参数的函数。 Func 包装了一个返回类型为 TR 的函数,以及两个类型分别为 T1 和 T2 的参数。等等......但是,请记住,通过使用像这样的预定义委托,参数名称不会描述它们必须传递的内容,委托名称对于它应该做的事情也没有意义。因此,在使用这些委托不会影响代码自我描述的好处时要小心,并避免在其目的不是绝对不言而喻的情况下使用它们。

Action 封装了一个无参数的 void 方法。

Action 用一个 T1 类型的参数包装了一个 void 方法。

Action 分别用两个 T1 和 T2 类型的参数包装了一个 void 方法。

等等……

Func 包装了一个带有 TR 返回类型的无参数函数。

Func 包装了一个具有 TR 返回类型和一个 T1 类型参数的函数。

Func 包装了一个返回类型为 TR 的函数,以及两个类型分别为 T1 和 T2 的参数。

等等……

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


不应该是 Func<T> 的返回类型吗?是最后一个吗? Func<T1,T2,TR>
D
Davide Cannizzo

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

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

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

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

这是非常有用的。通过这种方式,可以使用一个或多个参数。我想,最新的答案是这个。
我想添加有关此实现的一件事。如果您要传递的方法的返回类型为 void,则不能使用此解决方案。
@ImantsVolkovs 我相信您可以修改它以使用 Action 而不是 Func,并将签名更改为无效。虽然不是 100% 肯定。
有什么方法可以将参数传递给被调用的函数?
D
Davide Cannizzo

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

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

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

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

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


S
SteakOverflow

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

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

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

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 构造函数中,您需要创建父 //page 的“委托类型”参数

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

已编辑,但此答案的质量仍有待提高。
J
Junaid Pathan

如果要将 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");
}

J
Jeremy Samuel

这是一个没有参数的示例:http://en.csharp-online.net/CSharp_FAQ:_How_call_a_method_using_a_name_string

带参数:http://www.daniweb.com/forums/thread98148.html#

您基本上传入一个对象数组以及方法名称。然后将两者与 Invoke 方法一起使用。

参数 Object[] 参数


请注意,方法的名称不在字符串中——它实际上是一个方法组。代表在这里是最好的答案,而不是反思。
@Lette:在方法调用中,作为参数的表达式是一个方法组;它是编译时已知的方法的名称,编译器可以将其转换为委托。这与仅在执行时知道名称的情况有很大不同。
不要只是粘贴链接。链接必须作为参考,也不能省略。答案的第一个链接坏了,如果第二个坏了,你的答案就没有用了。用实际的内容让它有用,解决方案应该在这里,至少是它的核心。 “params Object[] 参数”是不够的。
佚名
class PersonDB
{
  string[] list = { "John", "Sam", "Dave" };
  public void Process(ProcessPersonDelegate f)
  {
    foreach(string s in list) f(s);
  }
}

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

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

r
rtgher

我不知道谁可能需要这个,但如果你不确定如何发送带有委托的 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."));
}

u
user18807217

如果传递的方法需要接受一个参数并返回一个值,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);
}

阅读文档here

但是,如果作为参数传递的方法没有返回任何内容,您也可以使用 Action。对于传递的方法,它最多支持 16 个参数。这是一个例子。 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);
}

阅读文档 here