繁体   English   中英

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

[英]Pass Method as Parameter using 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 代码,因为我需要定义参数。

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

您需要使用委托 在这种情况下,您的所有方法都采用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。 我有一篇文章主要是关于委托和事件之间的区别,但无论如何你可能会发现它很有用。


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

public delegate int MyDelegateType(string value)

然后将参数改为MyDelegateType类型。

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

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


简介

在 CLR(公共语言运行时)之上运行的所有语言,例如 C#、F# 和 Visual Basic,都在运行比机器代码更高级别代码的 VM 下工作。 与 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 标准的委托:

    • 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的参数。
    • 等等……

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

此处阅读文档

但是,如果作为参数传递的方法没有返回任何内容,您也可以使用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);
}

此处阅读文档

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM