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


115

Func<>它的作用是什么?


4
这只是具有特定签名的代表的快捷方式。要完全理解下面的答案,您需要理解代表;-)
Theo Lenndorff 2010年

2
在@Oded的回答中说If you have a function that needs to return different types, depending on the parameters, you can use a Func delegate, specifying the return type.
LCJ

Answers:


76

Func<T>是用于方法的预定义委托类型,该方法返回该类型的某些值T

换句话说,您可以使用此类型来引用返回值的方法T。例如

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

可以这样引用

Func<string> f = GetMessage;

但它也可以表示一个静态的一元函数=)
Ark-kun 2014年

2
@方舟坤不,那是不正确的。的定义Func<T>delegate TResult Func<out TResult>()。没有参数。Func<T1, T2>将是一个带有一个参数的函数。
Brian Rasmussen 2014年

4
不,我是对的。static int OneArgFunc(this string i) { return 42; } Func<int> f = "foo".OneArgFunc;。=)
方舟坤

1
这是一种特殊的扩展方法。
Brian Rasmussen 2014年

关于它的唯一特殊的事情Extension是仅由C#/ VB.Net编译器而不是CLR读取的属性。基本上,实例方法(与静态函数不同)具有隐藏的第0个“ this”参数。因此,1参数实例方法与2参数静态函数非常相似。然后,我们有代表存储目标对象和函数指针。代表可以将第一个参数存储在目标中,也可以不存储。
Ark-kun 2014年

87

将其视为占位符。当您的代码遵循某种特定模式但不必绑定到任何特定功能时,此功能将非常有用。

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

  • 图案是:在一个序列中的每个项目,选择从该项目(例如,属性)的一些值,并创建由这些值中的一个新的序列。
  • 占位符是:上述实际获取的值的序列的一些选择器功能。

此方法采用a 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);

在由代表担任占位符的情况下,我免除了不得不在类似情况下一遍又一遍地写出相同模式的麻烦。


66

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

等等

不过请记住,它是一个委托,有关更多高级信息,请参阅文档。


1
出色的答案,但必须填写关键字static
boctulus 2014年

16

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

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

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

例如,您想让他定义数字格式的特定类型,依此类推。

没有功能

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

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

使用Func更简单

在组件内部定义类型的一个参数Func<T,String>代表的功能的接口的是采用类型T的输入参数,并返回一个字符串(输出为控制台)

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>使您可以动态定义通用方法接口。 您可以定义输入是什么类型,以及输出是什么类型。简单明了。


2
感谢您发布此Marco。它确实帮助了我。一段时间以来,我一直试图了解func,并在我的编程中积极使用它。本示例将清除路径。我必须添加StampaFunc方法,因为它在原始代码中被遗漏了,从而阻止了它的显示。
西沃库·阿德奥拉

1
我认为Func示例中缺少一行,打印功能或StampaFunc的调用在哪里?
Bashar Abu Shamaa

11

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

如果您有一个需要根据参数返回不同类型的函数,则可以使用Func委托来指定返回类型。


7

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


0

也许添加一些信息还为时不晚。

和:

Func是在系统名称空间中定义的自定义委托,它允许您使用0到16个输入参数指向具有相同签名的方法(与委托一样)。

命名和使用方式:

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

定义:

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

使用位置:

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

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.