C# 传递函数作为参数

182

我已经在C#中编写了一个数值微分函数,看起来像这样:

public double Diff(double x)
{
    double h = 0.0000001;

    return (Function(x + h) - Function(x)) / h;
}

我希望能够像这样传入任何函数:

public double Diff(double x, function f)
{
    double h = 0.0000001;

    return (f(x + h) - f(x)) / h;
}

我认为使用委托可能是可行的,但我不确定如何使用它们。


1
这个回答解决了您的问题吗?使用C#将方法作为参数传递 - Davide Cannizzo
3个回答

199

在 .Net (v2 及以后版本) 中有一些通用类型,使得将函数作为委托进行传递非常容易。

对于有返回类型的函数,可以使用 Func<>,对于没有返回类型的函数,则可以使用 Action<>。

Func 和 Action 都可以声明为从 0 到 4 个参数。例如,Func<double, int> 接受一个 double 参数并返回一个 int。Action<double, double, double> 接受三个 double 参数并返回无(void)。

因此,您可以声明您的 Diff 函数接受一个 Func:

public double Diff(double x, Func<double, double> f) {
    double h = 0.0000001;

    return (f(x + h) - f(x)) / h;
}

然后,您可以这样调用它,只需为其提供符合您的 Func 或 Action 签名的函数名称即可:

double result = Diff(myValue, Function);

你甚至可以使用lambda语法将函数写成一行:

double result = Diff(myValue, d => Math.Sqrt(d * 3.14));

33
在.NET 4中,FuncAction都已更新,允许使用高达16个参数。 - Joel Mueller
5
дёҖдёӘеҫҲй…·зҡ„дәӢжғ…жҳҜиҝ”еӣһдёҖдёӘйҖҡиҝҮж•°еҖји®Ўз®—еҫ—еҮәзҡ„иҫ“е…ҘеҮҪж•°зҡ„дёҖйҳ¶еҜјж•°зҡ„Func<double,double>еҮҪж•°гҖӮд»Јз Ғдёәreturn x => (f(x + h) - f(x)) / h;пјҢдҪ з”ҡиҮіеҸҜд»Ҙзј–еҶҷдёҖдёӘйҮҚиҪҪеҮҪж•°жқҘиҝ”еӣһиҫ“е…ҘеҮҪж•°зҡ„nйҳ¶еҜјж•°гҖӮ - Ani

180

使用上面提到的 Func 是可行的,但也有委托(delegates)可以完成相同的任务,并且在命名中定义意图:

public delegate double MyFunction(double x);

public double Diff(double x, MyFunction f)
{
    double h = 0.0000001;

    return (f(x + h) - f(x)) / h;
}

public double MyFunctionMethod(double x)
{
    // Can add more complicated logic here
    return x + 10;
}

public void Client()
{
    double result = Diff(1.234, x => x * 456.1234);
    double secondResult = Diff(2.345, MyFunctionMethod);
}

7
在3.5版本及其之后,Func<>和delegate是可以互换的,这意味着可以使用匿名委托和lambda表达式(它们是匿名委托的语法糖)。因此,无论您将参数指定为Func<double,double>还是接受double并返回double的委托,都不会有太大区别。命名委托唯一的优势是能够添加XML文档注释;使用描述性名称与参数名称相比,实现起来同样容易。 - KeithS
6
我认为方法原型仍然比 Func<x, y> 更易读 - 这不仅是命名使代码易读,正如你所说,这并不妨碍你将 Lambda 表达式传递到代码中。 - Ian Johnson
如果委托类型的命名对于代码的清晰度非常重要,我认为在大多数情况下,我会倾向于使用接口和实现。 - quentin-starin
@qstarin,这不仅涉及到委托的命名,还包括方法必须采用的参数命名,特别是如果它们只是本地类型。你是对的,我大多数情况下使用接口而不是委托。 - Ian Johnson
如果我正在创建一个提供常见功能的通用函数(带有不太常见的返回类型),一切都正常,直到我尝试将其与void一起使用。我真的需要使用Action而不是Func来创建重复函数吗?还是有更好的方法? - Dan Chase
对于不熟悉委托的人来说,这是一个相当深奥的例子。使用Lambda表达式真的必要吗? - David Bandel

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

使用方法:

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

1
我很好奇,为什么要使用泛型类型参数? - kdbanman
4
我遇到了这个错误:无法从使用中推断出方法“Runner<T>(Func<T>)”的类型参数。请尝试显式指定类型参数。 - DermFrench
你的“funcToRun”方法签名是什么? - kravits88

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接