本地函数与 Lambda C# 7.0

Posted

技术标签:

【中文标题】本地函数与 Lambda C# 7.0【英文标题】:Local function vs Lambda C# 7.0 【发布时间】:2017-04-18 00:11:45 【问题描述】:

我正在查看C# 7.0 中的新实现,我发现它们实现了本地函数很有趣,但我无法想象本地函数比 lambda 表达式更受青睐的场景,以及两者之间的区别是什么两个。

我确实知道 lambda 是 anonymous 函数,而本地函数不是,但我无法弄清楚一个真实世界的场景,其中本地函数比 lambda 表达式具有优势

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

【问题讨论】:

泛型、输出参数、递归函数,无需将lambda初始化为null等 @KirkWoll - 您应该将此作为答案发布。 【参考方案1】:

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

你想要一个辅助函数。您仅在单个函数中使用它,并且它可能使用在该包含函数范围内的变量和类型参数。另一方面,与 lambda 不同的是,您不需要将它作为第一类对象,因此您不需要给它一个委托类型并分配一个实际的委托对象。此外,您可能希望它是递归的或通用的,或者将其实现为迭代器。

进一步扩展,优点是:

    性能。

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

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

    这也意味着调用本地函数更便宜,它们可以内联,可能会进一步提高性能。

    局部函数可以递归。

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

    本地函数可以是泛型的。

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

    本地函数可以实现为迭代器。

    Lambdas 不能使用yield return(和yield break)关键字来实现@​​987654327@-returning 函数。本地函数可以。

    局部函数看起来更好。

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

    比较:

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

【讨论】:

我想补充一点,本地函数在调用方有参数名称。 Lambda 没有。 @Lensflare 确实没有保留 lambda 的参数名称,但那是因为它们必须转换为具有自己名称的委托。例如:Func&lt;int, int, int&gt; f = (x, y) =&gt; x + y; f(arg1:1, arg2:1);. 很棒的清单!但是,我可以想象 IL/JIT 编译器如何执行 1 中提到的所有优化。如果委托的使用符合某些规则,也可以用于委托。 @Casebash 因为 lambdas 总是使用一个委托,并且该委托将闭包保存为 object。所以,lambdas 可以使用一个结构,但它必须被装箱,所以你仍然会有额外的分配。 @happybits 主要是当你不需要给它命名时,比如当你将它传递给方法时。【参考方案2】:

除了svick's great answer,本地函数还有一个优势: 它们可以在函数中的任何位置定义,即使在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;

【讨论】:

这真的很有用,因为我可以习惯将所有辅助函数放在函数底部的 #region Helpers 中,这样可以避免该函数中的混乱,尤其是避免主函数中的混乱类。 我也很感激。它使您正在查看的主要功能更易于阅读,因为您无需四处寻找它的开始位置。如果您想查看实现细节,请继续往下看。 如果你的函数太大以至于它们需要区域,那么它们就太大了。【参考方案3】:

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

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 的链接。

免责声明。我是负责JustMock的开发人员之一。

【讨论】:

【参考方案4】:

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

我们所做的就是打开一个到服务器的套接字连接,然后循环将一个事件绑定到一个事件的数据。可以将其视为与设计类相同的方式,只有一个不是到处编写真正仅用于一种功能的帮助方法。下面是一些示例,请注意我正在使用变量,并且“帮助”方法在 finally 的下方。在最后我很好地删除了事件处理程序,如果我的 Exchange 类是外部/注入的,我将不会注册任何挂起的事件处理程序

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;
    

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

【讨论】:

1.这是一个非常复杂的示例和解释,只是为了演示本地功能。 2. 与本例中的 lambdas 相比,局部函数并没有避免任何分配,因为它们仍然必须转换为委托。所以我看不出他们会如何避免 GC。 不传递/复制变量,svick 的回答很好地涵盖了其余部分。无需重复他的答案

以上是关于本地函数与 Lambda C# 7.0的主要内容,如果未能解决你的问题,请参考以下文章

如何让我的Lambda函数与在本地计算机上运行的服务器通信?

如何在本地测试 aws lambda 函数

C# 7.0

只在本地使用 lambda 可以吗? [关闭]

C# 本地函数-

Lambda表达式