什么是 Func,如何以及何时使用它

什么是 Func<>? 它的用途是什么?

143624 次浏览

Func<T>是返回类型为 T的某个值的方法的预定义委托类型。

换句话说,您可以使用这个类型来引用一个方法,该方法返回一些 T的值。

public static string GetMessage() { return "Hello world"; }

可以这样引用

Func<string> f = GetMessage;

Func<T1,R>和其他预定义的通用 Func委托(Func<T1,T2,R>Func<T1,T2,T3,R>和其他)是返回最后一个通用参数类型的通用委托。

如果有一个函数需要返回不同的类型(取决于参数) ,则可以使用 Func委托,指定返回类型。

把它想成一个占位符。如果您的代码遵循某种模式,但不需要绑定到任何特定的功能,那么它就非常有用。

例如,考虑 Enumerable.Select扩展方法。

  • 模式是: 对于序列中的每个项,从该项中选择一些值(例如,一个属性)并创建一个由这些值组成的新序列。
  • 占位符是: 某个选择器函数,它实际获取上述序列的值。

此方法使用 Func<T, TResult>代替任何具体函数。这允许它在应用上述模式的 任何上下文中使用。

例如,假设我有一个 List<Person>,我只需要列表中每个人的名字。我能做到:

var names = people.Select(p => p.Name);

或者说我想要每个人的 年龄:

var ages = people.Select(p => p.Age);

现在,您可以看到我是如何利用表示 模式一样代码(使用 Select)和两个 与众不同函数(p => p.Namep => p.Age)的。

另一种方法是,每次您想要扫描一个序列以获得不同类型的值时,都编写一个不同版本的 Select。因此,要达到与上述相同的效果,我需要:

// Presumably, the code inside these two methods would look almost identical;
// the only difference would be the part that actually selects a value
// based on a Person.
var names = GetPersonNames(people);
var ages = GetPersonAges(people);

有了一个代表作为占位符,我就不必在这种情况下一遍又一遍地写出相同的模式。

Func<T1, T2, ..., Tn, Tr>表示一个函数,它接受(T1,T2,... ,Tn)参数并返回 Tr。

例如,如果你有一个函数:

double sqr(double x) { return x * x; }

你可以把它保存为某种函数变量:

Func<double, double> f1 = sqr;
Func<double, double> f2 = x => x * x;

然后使用与 sqr 完全一样的方法:

f1(2);
Console.WriteLine(f2(f1(4)));

等等。

请记住,它是一个委托,更高级的信息请参考文档。

它只是一个预定义的泛型委托。使用它,您不需要声明每个委托。还有另一个预定义的委托 Action<T, T2...>,它是相同的,但返回 void。

我发现 Func<T>非常有用,当我创建一个组件,需要个性化的“在飞行中”。

以这个非常简单的例子为例: 一个 PrintListToConsole<T>组件。

一个非常简单的对象,它将这个对象列表打印到控制台。 您希望让使用它的开发人员对输出进行个性化处理。

例如,您希望让他定义特定类型的数字格式等等。

没有 Func

首先,您必须为接受输入并生成要打印到控制台的字符串的类创建一个接口。

interface PrintListConsoleRender<T> {
String Render(T input);
}

然后,您必须创建类 PrintListToConsole<T>,该类接受以前创建的接口,并在列表的每个元素上使用它。

class PrintListToConsole<T> {


private PrintListConsoleRender<T> _renderer;


public void SetRenderer(PrintListConsoleRender<T> r) {
// this is the point where I can personalize the render mechanism
_renderer = r;
}


public void PrintToConsole(List<T> list) {
foreach (var item in list) {
Console.Write(_renderer.Render(item));
}
}
}

需要使用组件的开发人员必须:

  1. 实现接口

  2. 将真实类传递给 PrintListToConsole

    class MyRenderer : PrintListConsoleRender<int> {
    public String Render(int input) {
    return "Number: " + input;
    }
    }
    
    
    class Program {
    static void Main(string[] args) {
    var list = new List<int> { 1, 2, 3 };
    var printer = new PrintListToConsole<int>();
    printer.SetRenderer(new MyRenderer());
    printer.PrintToConsole(list);
    string result = Console.ReadLine();
    }
    }
    

Using Func it's much simpler

Inside the component you define a parameter of type Func<T,String> that represents an interface of a function that takes an input parameter of type T and returns a string (the output for the console)

class PrintListToConsole<T> {


private Func<T, String> _renderFunc;


public void SetRenderFunc(Func<T, String> r) {
// this is the point where I can set the render mechanism
_renderFunc = r;
}


public void Print(List<T> list) {
foreach (var item in list) {
Console.Write(_renderFunc(item));
}
}
}

当开发人员使用您的组件时,他只需将 Func<T, String>类型的实现传递给组件,这是一个为控制台创建输出的函数。

class Program {
static void Main(string[] args) {
var list = new List<int> { 1, 2, 3 }; // should be a list as the method signature expects
var printer = new PrintListToConsole<int>();
printer.SetRenderFunc((o) => "Number:" + o);
printer.Print(list);
string result = Console.ReadLine();
}
}

Func<T>允许您动态定义一个通用方法接口。 您定义输入的类型和输出的类型。 简洁明了。

也许现在添加一些信息还不算太晚。

总结:

Func 是在 System 命名空间中定义的自定义委托,它允许您指向具有相同签名的方法(与委托一样) ,使用0到16个输入参数,并且必须返回某些内容。

术语及使用方法:

Func<input_1, input_2, ..., input1_6, output> funcDelegate = someMethod;

定义:

public delegate TResult Func<in T, out TResult>(T arg);

使用地点:

它用于 lambda 表达式和匿名方法。

C # 和 Java 都没有纯函数,只有成员函数(aka 方法)。而且这些方法都不是一等公民。一流的函数允许我们创建漂亮而强大的代码,如 F # 或 Clojure 语言所示。(例如,第一类函数可以作为参数传递,并且可以返回函数。)Java 和 C # 通过接口/委托在一定程度上改进了这一点。

Func<int, int, int> randInt = (n1, n2) => new Random().Next(n1, n2);

因此,Func是一个内置的委托,它带来了一些函数式编程特性,并有助于减少代码冗长。

上面提到的答案非常棒,只是提出几点我看到的可能会有帮助:

  • Func 是内置的委托类型

  • Func 委托类型必须返回一个值。如果不需要返回类型,请使用 开拍委托。

  • Func 委托类型可以有0到16个输入参数。

  • Func 委托不允许 ref 和 out 参数。

  • Func 委托类型可以与匿名方法或 lambda 表达式一起使用。

    Func < int,int,int > Sum = (x,y) = > x + y;