这个 Func<T, TReturn> 中的第一个箭头运算符是什么意思?

What does the first arrow operator in this Func<T, TReturn> mean?

鉴于此示例代码:

enum op
{ 
  add, 
  remove
}

Func<op, int> combo(string head, double tail) => 
  (op op) => 
  op == op.add ? Int32.Parse(head) + Convert.ToInt32(tail) : Int32.Parse(head) - Convert.ToInt32(tail);

Console.WriteLine(combo("1", 2.5)(op.remove));

哪个returns:

-1

第一个箭头运算符是什么意思?
根据规范,它看起来不像表达式主体或 lambda 运算符。
C# language specification 中有关于此用法的任何参考吗?

函数returns函数,不用lambda可能更容易理解,写成"old"方式,只是为了解释,认为params闭包不理想:

        string head;
        double tail;
        private int innerFunc(op op )
        {
            return op == op.add ? Int32.Parse(head) + Convert.ToInt32(tail) : Int32.Parse(head) - Convert.ToInt32(tail);
        }

        Func<op, int> comboWihoutLambda (string headprm, double tailprm)
        {
            head = headprm;
            tail = tailprm;
            return innerFunc;

        }

What does the first arrow operator mean?

在较新版本的 C# 中,您可以编写:

int M(int x) 
{
  return x + x;
}

更短更清晰:

int M(int x) => x + x;

无非就是一个"syntactic sugar"让你写一个简单的方法更短更直接

it does not look like an expression body or a lambda operator.

左边=>表示表达式体。右边的=>表示一个lambda。所以在你的例子中有点混乱,因为返回的东西是一个 lambda,它也使用 =>。但不要让它分散你的注意力:

Func<int, int> M() => x => x + 1;

只是简写

Func<int, int> M() 
{
  return x => x + 1;
}

这反过来只是一个短途或写作

static int Anonymous(int x)
{
  return x + 1;
}
Func<int, int> M() 
{
  return Anonymous;
}

如果您发现包含多个 => 运算符的代码令人困惑,您始终可以通过将它们脱糖为更明确的形式来删除它们。这只是一些人觉得更容易阅读的简短形式。


Can you please write

static Func<op, int> combo(string head, double tail) =>
  (op op) => ...; 

with just one => operator.

当然可以。这是编写该代码的两种等效方法。

// Block body, lambda return.
static Func<op, int> combo(string head, double tail)
{
  return (op op) => ...; 
}

// Block body, local function return:
static Func<op, int> combo(string head, double tail)
{
  op MyFunction(op)
  {
     return ...;
  }
  return MyFunction;
}

Sorry, I was not being explicit. What I meant was can you please extract the lambda into its own standalone function, like what one would do in the olden days.

当然;这有点复杂。我们将通过一系列小步骤来完成:

我们从这个开始:

Func<op, int> combo(string head, double tail) => 
  (op op) => 
    op == op.add ? Int32.Parse(head) + Convert.ToInt32(tail) : 
    Int32.Parse(head) - Convert.ToInt32(tail);

对外部函数进行脱糖:

Func<op, int> combo(string head, double tail)
{
  return (op op) => 
    op == op.add ? Int32.Parse(head) + Convert.ToInt32(tail) : 
    Int32.Parse(head) - Convert.ToInt32(tail);
}

将内部函数变成辅助函数:

static int Helper(string head, double tail, op op)
{
    return op == op.add ? Int32.Parse(head) + Convert.ToInt32(tail) : 
    Int32.Parse(head) - Convert.ToInt32(tail);
}

Func<op, int> combo(string head, double tail)
{
  return (op op) => Helper(head, tail, op);
}

现在将辅助方法移动到 class:

private class Closure
{
  public int Helper(string head, double tail, op op)
  {
    return op == op.add ? Int32.Parse(head) + Convert.ToInt32(tail) : 
    Int32.Parse(head) - Convert.ToInt32(tail);
  }
}

Func<op, int> combo(string head, double tail)
{
  Closure c = new Closure();
  return (op op) => c.Helper(head, tail, op);
}

现在创建闭包的头部和尾部成员:

private class Closure
{
  public string head;
  public double tail;
  public int Helper(op op)
  {
    return op == op.add ? Int32.Parse(head) + Convert.ToInt32(tail) : 
    Int32.Parse(head) - Convert.ToInt32(tail);
  }
}

Func<op, int> combo(string head, double tail)
{
  Closure c = new Closure();
  c.head = head;
  c.tail = tail;
  return (op op) => c.Helper(op);
}

现在我们可以对 lambda 进行脱糖了:

private class Closure
{
  public string head;
  public double tail;
  public int Helper(op op)
  {
    return op == op.add ? Int32.Parse(head) + Convert.ToInt32(tail) : 
    Int32.Parse(head) - Convert.ToInt32(tail);
  }
}

Func<op, int> combo(string head, double tail)
{
  Closure c = new Closure();
  c.head = head;
  c.tail = tail;
  return c.Helper;
}

我们完成了。

如果您使用 ILDASM 或 sharplab.io 或其他类似工具反编译您的代码,您会发现这正是编译器为您的代码生成的内容,除了它会为助手生成奇怪的非法名称以确保你永远不会不小心错误地调用其中之一。