本地函数与 Lambda C# 7.0

Local function vs Lambda C# 7.0

我正在查看 C# 7.0 中的新实现,我发现它们实现了局部函数很有趣,但我无法想象局部函数比 lambda 表达式更受欢迎的场景,什么是两者的区别

我知道 lambda 是 anonymous 函数,而局部函数不是,但我想不出在现实世界中局部函数比 lambda 表达式有优势的场景

任何例子将不胜感激。谢谢

This was explained by Mads Torgersen in C# Design Meeting Notes where local functions were first discussed:

You want a helper function. You are only using it from within a single function, and it likely uses variables and type parameters that are in scope in that containing function. On the other hand, unlike a lambda you don't need it as a first class object, so you don't care to give it a delegate type and allocate an actual delegate object. Also you may want it to be recursive or generic, or to implement it as an iterator.

进一步扩展它的优点是:

  1. 性能。

    创建 lambda 时,必须创建委托,在这种情况下这是不必要的分配。局部函数实际上只是函数,不需要委托。

    此外,局部函数在捕获局部变量方面更有效:lambdas 通常将变量捕获到 class,而局部函数可以使用结构(使用 ref 传递),这再次避免了分配。

    这也意味着调用本地函数的成本更低,并且可以内联,从而可能进一步提高性能。

  2. 局部函数可以递归。

    Lambdas 也可以递归,但它需要笨拙的代码,您首先将 null 分配给委托变量,然后再分配给 lambda。局部函数自然可以递归(包括互递归)。

  3. 局部函数可以是通用的。

    Lambda 不能是泛型的,因为它们必须分配给具有具体类型的变量(该类型可以使用外部作用域中的泛型变量,但这不是一回事)。

  4. 局部函数可以作为迭代器实现。

    Lambdas 不能使用 yield return(和 yield break)关键字来实现 IEnumerable<T>-返回函数。局部函数可以。

  5. 局部函数看起来更好。

    这在上面的引用中没有提到,可能只是我个人的偏见,但我认为普通函数语法看起来比将 lambda 分配给委托变量更好。局部函数也更简洁。

    比较:

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

除了之外,本地函数还有一个优势:
它们可以在函数的任何地方定义,甚至在 return 语句之后。

public double DoMath(double a, double b)
{
    var resultA = f(a);
    var resultB = f(b);
    return resultA + resultB;

    double f(double x) => 5 * x + 3;
}

我使用内联函数来避免垃圾收集压力,特别是在处理较长的 运行 方法时。假设有人想获得给定股票代码的 2 年或市场数据。此外,如果需要,可以打包很多功能和业务逻辑。

我们所做的是打开一个到服务器的套接字连接并遍历将一个事件绑定到另一个事件的数据。人们可以用与设计 class 相同的方式来思考它,只有一个人没有在所有地方编写实际上只为一种功能工作的辅助方法。下面是一些示例,请注意我使用的是变量,"helper" 方法在 finally 下方。在最后,我很好地删除了事件处理程序,如果我的 Exchange class 将是 external/injected 我将不会注册任何待处理的事件处理程序

void List<HistoricalData> RequestData(Ticker ticker, TimeSpan timeout)
{
    var socket= new Exchange(ticker);
    bool done=false;
    socket.OnData += _onData;
    socket.OnDone += _onDone;
    var request= NextRequestNr();
    var result = new List<HistoricalData>();
    var start= DateTime.Now;
    socket.RequestHistoricalData(requestId:request:days:1);
    try
    {
      while(!done)
      {   //stop when take to long….
        if((DateTime.Now-start)>timeout)
           break;
      }
      return result;

    }finally
    {
        socket.OnData-=_onData;
        socket.OnDone-= _onDone;
    }


   void _OnData(object sender, HistoricalData data)
   {
       _result.Add(data);
   }
   void _onDone(object sender, EndEventArgs args)
   {
      if(args.ReqId==request )
         done=true;
   } 
}

您可以看到下面提到的优点,在这里您可以看到一个示例实现。希望这有助于解释好处。

如果您还想知道如何测试本地函数,您应该查看 JustMock,因为它具有执行此操作的功能。这是一个将要测试的简单 class 示例:

public class Foo // the class under test
{ 
    public int GetResult() 
    { 
        return 100 + GetLocal(); 
        int GetLocal () 
        { 
            return 42; 
        } 
    } 
}

测试结果如下:

[TestClass] 
public class MockLocalFunctions 
{ 
    [TestMethod] 
    public void BasicUsage() 
    { 
        //Arrange 
        var foo = Mock.Create<Foo>(Behavior.CallOriginal); 
        Mock.Local.Function.Arrange<int>(foo, "GetResult", "GetLocal").DoNothing(); 

        //Act 
        var result = foo. GetResult(); 

        //Assert 
        Assert.AreEqual(100, result); 
    } 
} 

这是 JustMock documentation 的 link。

免责声明。我是负责 JustMock.

的开发人员之一