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

use*_*673 646 .net c# 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);
}
Run Code Online (Sandbox Code Playgroud)

此代码不起作用,但这是我想要做的.我不明白的是如何编写RunTheMethod代码,因为我需要定义参数.

Egi*_*sen 796

您可以使用.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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @unknown:在这种情况下,它将是`Action`而不是`Func <string,int>`. (196认同)
  • 如果Method具有返回void和无参数的签名,Func调用将如何更改?我似乎无法使语法工作. (45认同)
  • @ user396483例如,`Action <int,string>`对应于一个采用2个参数(int和string)并返回void的方法. (37认同)
  • @NoelWidmer使用`Func <double,string,int>`对应一个带有2个参数(`double`和`string`)并返回`int`的方法.最后指定的类型是返回类型.您可以将此委托用于最多16个参数.如果您需要更多,请将您自己的委托编写为`公共委托TResult Func <在T1中,在T2中,(尽可能多的参数),在Tn中,输出TResult>(T1 arg1,T2 arg2,...,Tn ARGN);`.如果我误解了,请纠正我. (23认同)
  • 但是现在如果你想将参数传递给方法怎么办? (11认同)
  • 有没有听说过OOP?请将对象作为第二个参数传递. (4认同)

Jon*_*eet 345

您需要使用委托.在这种情况下,所有方法都接受一个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;
}
Run Code Online (Sandbox Code Playgroud)

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

RunTheMethod(x => x.Length);
Run Code Online (Sandbox Code Playgroud)

这将创建一个这样的匿名函数:

// 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;
}
Run Code Online (Sandbox Code Playgroud)

然后将该委托传递给该RunTheMethod方法.

您可以使用委托进行事件订阅,异步执行,回调 - 各种事情.非常值得阅读它们,特别是如果你想使用LINQ.我有一个文章大多是关于委托和事件之间的差异,但你会发现它很有用呢.


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

public delegate int MyDelegateType(string value)
Run Code Online (Sandbox Code Playgroud)

然后使参数成为类型MyDelegateType.

  • +1这真是一个令人惊讶的答案,在两分钟内发出嘎嘎声. (55认同)
  • @Paolo:代表只是一个非常方便的策略模式实现,其中所讨论的策略只需要一个方法.这并不像是对抗*策略模式 - 但它比使用接口实现模式更方便. (19认同)
  • "经典"代表(从.NET 1/2中已知)仍然有用,还是因为Func/Action而完全过时?另外,你的例子中没有一个委托关键字`public**delegate**int MyDelegateType(string value)`? (5认同)
  • 虽然您可以使用委托传递函数,但更传统的OO方法是使用策略模式. (3认同)
  • @RaduSimionescu:啊。您不清楚 - 您专门询问了“Func&lt;T, TResult&gt;”和“Action”,而不是“Func”和“Action”委托的*组*。但不,区别不在于输入,而在于*输出*。`Func` 委托都有一个返回类型(这是类型参数之一)。`Action` 委托都是 `void`。 (3认同)
  • @RaduSimionescu:嗯,不 - `Func&lt;T, TResult&gt;` 接受一个 `T` 并返回一个 `TResult`。`Action` 不接受任何内容,也不返回任何内容。 (2认同)
  • @JonSkeet:首先,精彩的文章。对编辑的谦虚建议:当我阅读将 lambda 转换为匿名函数的部分时,看到了这一点: private static int &lt;&gt;_HiddenMethod_&lt;&gt;(string x) { ... } 我很困惑一分钟,因为 &lt;&gt; 当然用于泛型。我花了几分钟将它粘贴到 C# 中,看看我是否遗漏了什么,然后意识到您可能只是在标记动态部分。改变这一点可能会让其他人平静下来。从中学到了很多,谢谢! (2认同)
  • @BrianMacKay:这样写是因为编译器对生成的成员使用了类似的名称,这些名称不应直接调用。如果您查看 IL,这就是您会看到的内容。不过会加注。 (2认同)

Zai*_*Ali 104

从OP的例子来看:

 public static int Method1(string mystring)
 {
      return 1;
 }

 public static int Method2(string mystring)
 {
     return 2;
 }
Run Code Online (Sandbox Code Playgroud)

你可以试试Action Delegate!然后使用调用您的方法

 public bool RunTheMethod(Action myMethodName)
 {
      myMethodName();   // note: the return value got discarded
      return true;
 }

RunTheMethod(() => Method1("MyString1"));
Run Code Online (Sandbox Code Playgroud)

要么

public static object InvokeMethod(Delegate method, params object[] args)
{
     return method.DynamicInvoke(args);
}
Run Code Online (Sandbox Code Playgroud)

然后简单地调用方法

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

Console.WriteLine(InvokeMethod(new Func<string, int>(Method2), "MyString2"));
Run Code Online (Sandbox Code Playgroud)

  • 谢谢,这让我想去,因为我想要一个允许多个参数的更通用的"RunTheMethod"方法.顺便说一句,你的第一个`InvokeMethod` lambda调用应该是`RunTheMethod` (4认同)
  • 您过得很愉快;)比选择的答案IMO真正易于使用并且更加灵活。 (2认同)

kra*_*s88 30

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

用法:

var ReturnValue = Runner(() => GetUser(99));
Run Code Online (Sandbox Code Playgroud)

  • 这非常有用.通过这种方式,可以使用一个或多个参数.我想,最新的答案就是这个. (6认同)
  • 有什么方法可以将参数传递给被调用的函数吗? (2认同)

Bru*_*eis 12

你应该使用一个Func<string, int>委托,它代表一个带有stringas参数的函数并返回一个int:

public bool RunTheMethod(Func<string, int> myMethod) {
    // do stuff
    myMethod.Invoke("My String");
    // do stuff
    return true;
}
Run Code Online (Sandbox Code Playgroud)

然后使用它:

public bool Test() {
    return RunTheMethod(Method1);
}
Run Code Online (Sandbox Code Playgroud)

  • 这不会编译.`Test`方法应该是`return RunTheMethod(Method1);` (3认同)

Dav*_*zzo 9

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


简单的介绍

所有CLR(公共语言运行时)语言(例如C#和Visual Basic)都在名为CLI(公共语言解释器)的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);
}
Run Code Online (Sandbox Code Playgroud)

三种方式:

  • 方法1直接
    使用Delegate特殊类作为上例.此解决方案的问题是,在动态传递参数时,将取消选中代码,而不会将它们限制为方法声明中的类型.

  • 方式2/3 除了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");
}
Run Code Online (Sandbox Code Playgroud)

通过这种方式不写自己的自定义委托的第二个选项是使用在系统库中声明的其中一个:

  • Action包装a void没有参数.
  • Action<T1>void用一个参数包装一个.
  • Action<T1, T2>void用两个参数包装一个.
  • 等等...
  • Func<TR>包装一个TR返回类型且没有参数的函数.
  • Func<T1, TR>使用TR返回类型和一个参数包装函数.
  • Func<T1, T2, TR>使用TR返回类型和两个参数包装函数.
  • 等等...

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

  • Func&lt;T&gt; 的返回类型不应该是最后一个吗?`Func&lt;T1,T2,TR&gt;` (2认同)

Mad*_*her 6

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

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