C#将函数作为参数传递


141

我已经用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;
}

我认为代表可以这样做(也许吗?),但是我不确定如何使用它们。

任何帮助将不胜感激。

Answers:


146

使用上面提到的Func可以工作,但是也有代表可以执行相同的任务,并在命名中定义意图:

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

5
在3.5及更高版本中,Func <>和委托是可互换的,这意味着还可以使用匿名委托和lambda(匿名委托的语法糖)。因此,将参数指定为Func <double,double>还是接受double并返回double的委托并不重要。命名委托给您的唯一真正好处是可以添加xml-doc注释;描述性名称与参数名称(而不是类型)一样容易实现。
KeithS

3
我认为方法原型仍然使代码比Func <x,y>更具可读性-不仅仅是使代码可读的命名,而且正如您所说,这并不阻止您将lambda传递给代码。
伊恩·约翰逊

如果委托类型的命名对代码清晰性如此重要,那么我认为在大多数情况下,我倾向于使用接口和实现。
quentin-starin's

@qstarin不仅是委托的命名,而且是方法必须采用的参数的命名,尤其是当它们只是本机类型时。您是对的,主要是我在委托上使用接口
Ian Johnson

如果我将其中之一作为提供通用功能的通用功能(具有不常见的返回类型),那么一切都会起作用,直到我尝试将其与void一起使用为止。我是否真的需要使用Action而不是Func来创建重复函数,还是有更好的方法?
丹·蔡斯

174

.Net(v2和更高版本)中有几种通用类型,使作为委托传递函数非常容易。

对于具有返回类型的函数,有Func <>;对于没有返回类型的函数,则有Action <>。

Func和Action都可以声明为0到4个参数。例如,Func <double,int>以一个double作为参数并返回一个int。动作<double,double,double>将三个double作为参数,但不返回任何值(无效)。

因此,您可以声明您的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));

28
在.NET 4中,FuncAction都已更新,最多允许16个参数。
乔尔·穆勒

5
要做的一件很酷的事情是返回a Func<double, double>,它是输入函数的一阶导数,当然是用数值计算的。return x => (f(x + h) - f(x)) / h;您甚至可以编写一个重载n函数,该重载函数返回输入函数的三阶导数。
阿妮(Ani)2010年

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

用法:

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

我很好奇,为什么要使用泛型类型参数?
kdbanman 2015年

2
我收到此错误:无法从用法中推断出方法'Runner <T>(Func <T>)'的类型参数。尝试显式指定类型参数。
DermFrench 2015年

您的“ funcToRun”方法签名是什么?
kravits88
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.