【问题标题】:What is Func, how and when is it used什么是 Func,如何以及何时使用
【发布时间】:2011-04-07 04:28:56
【问题描述】:

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

【问题讨论】:

  • 这只是具有特定签名的代表的快捷方式。要完全理解下面的答案,您需要了解代表 ;-)
  • 在@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.

标签: c# .net delegates


【解决方案1】:

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

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

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

此方法采用Func<T, TResult> 而不是任何具体函数。这允许它在上述模式适用的任何上下文中使用。

例如,假设我有一个List<Person>,我只想要列表中每个人的姓名。我可以这样做:

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

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

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

马上,您就可以看到我是如何利用 same 代码来表示 pattern(使用 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);

使用代理作为占位符,我不必在这种情况下一遍又一遍地写出相同的模式。

【讨论】:

    【解决方案2】:

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

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

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

    可以这样引用

    Func<string> f = GetMessage;
    

    【讨论】:

    • 但也可以表示静态的单参函数=)
    • @Ark-kun 不,这不正确。 Func&lt;T&gt; 的定义是delegate TResult Func&lt;out TResult&gt;()。没有争论。 Func&lt;T1, T2&gt; 是一个接受一个参数的函数。
    • 不,我是对的。 static int OneArgFunc(this string i) { return 42; }Func&lt;int&gt; f = "foo".OneArgFunc;。 =)
    • 这是一种特殊的扩展方法。
    • 唯一特别的是Extension 属性,它只能被 C#/VB.Net 编译器读取,CLR 不能。基本上,实例方法(与静态函数不同)有一个隐藏的第 0 个“this”参数。因此,1 参数实例方法与 2 参数静态函数非常相似。然后,我们有存储目标对象和函数指针的委托。代表可以将第一个参数存储在 target 中,也可以不这样做。
    【解决方案3】:

    Func&lt;T1, T2, ..., Tn, Tr&gt; 表示一个函数,它接受 (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)));
    

    等等

    但请记住,它是一个代表,有关更多高级信息,请参阅文档。

    【讨论】:

    • 优秀的答案,但需要编译关键字静态
    【解决方案4】:

    我发现Func&lt;T&gt; 在创建需要“即时”个性化的组件时非常有用。

    举这个非常简单的例子:PrintListToConsole&lt;T&gt; 组件。

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

    比如你想让他定义一个特定类型的数字格式等等。

    没有功能

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

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

    然后您必须创建类PrintListToConsole&lt;T&gt;,该类采用先前创建的接口并在列表的每个元素上使用它。

    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&lt;T,String&gt; 类型的参数,表示函数的接口,该函数接受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&lt;T, String&gt; 类型的实现传递给组件,这是一个为控制台创建输出的函数。

    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&lt;T&gt; 可让您动态定义通用方法接口。 您定义输入是什么类型以及输出是什么类型。 简洁明了。

    【讨论】:

    • 感谢您发布此 Marco。它真的帮助了我。我一直在尝试理解 func 并在我的编程中积极使用它。此示例将清除路径。我不得不添加 StampaFunc 方法,因为它在原始代码中被遗漏了,导致它无法显示。
    • 我认为 Func 示例中遗漏了一行,print function 或 StampaFunc 的调用在哪里?
    【解决方案5】:

    Func&lt;T1,R&gt; 和其他预定义的泛型 Func 委托(Func&lt;T1,T2,R&gt;Func&lt;T1,T2,T3,R&gt; 等)是泛型委托,它们返回最后一个泛型参数的类型。

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

    【讨论】:

      【解决方案6】:

      它只是一个预定义的通用委托。使用它,您无需声明每个委托。还有另一个预定义的委托,Action&lt;T, T2...&gt;,它是相同的,但返回 void。

      【讨论】:

        【解决方案7】:

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

        总和:

        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 表达式和匿名方法。

        【讨论】:

          【解决方案8】:

          C# 和 Java 都没有普通函数,只有成员函数(又名方法)。而且这些方法不是一等公民。一流的函数使我们能够创建漂亮而强大的代码,就像在 F# 或 Clojure 语言中看到的那样。 (例如,一等函数可以作为参数传递,也可以返回函数。)Java 和 C# 通过接口/委托在一定程度上改善了这一点。

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

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

          【讨论】:

            【解决方案9】:

            上述答案很棒,只是提出我看到的几点可能会有所帮助:

            • Func 是内置的委托类型

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

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

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

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

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2016-11-21
              • 2011-12-27
              • 1970-01-01
              • 1970-01-01
              • 2022-01-10
              • 2015-03-13
              • 2013-10-25
              • 2014-11-19
              相关资源
              最近更新 更多