本地功能与Lambda C#7.0

Sid*_*Sid 161 c# lambda function c#-7.0

我正在研究C#7.0中的新实现,我觉得有趣的是他们已经实现了本地函数,但我无法想象一个局部函数比lambda表达式更受欢迎的场景,两者之间有什么区别.

我知道lambdas是anonymous函数,而局部函数却没有,但我无法弄清楚一个真实世界的场景,其中局部函数优于lambda表达式

任何例子都将非常感激.谢谢.

svi*_*ick 256

Mads Torgersen在C#设计会议笔记中对此进行了解释,其中首先讨论了本地函数:

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

为了进一步扩展它,优点是:

  1. 性能.

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

    此外,本地函数在捕获局部变量时更有效:lambdas通常将变量捕获到类中,而本地函数可以使用struct(传递使用ref),这又避免了分配.

    这也意味着调用本地函数更便宜并且可以内联,可能进一步提高性能.

  2. 本地函数可以递归.

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

  3. 本地功能可以是通用的.

    Lambdas不能是通用的,因为它们必须被赋值给具有具体类型的变量(该类型可以使用来自外部作用域的泛型变量,但这不是同一个东西).

  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;
    
    Run Code Online (Sandbox Code Playgroud)

  • 我想补充一点,本地函数在调用者端有参数名称.兰巴达没有. (16认同)
  • @Lensflare 确实不保留 lambda 的参数名称,但这是因为它们必须转换为具有自己名称的委托。例如:`Func&lt;int, int, int&gt; f = (x, y) =&gt; x + y; f(arg1:1, arg2:1);`. (3认同)

Tim*_*ann 76

除了svick的好答案之外,本地函数还有一个优点:
它们可以在函数的任何地方定义,甚至在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;
}
Run Code Online (Sandbox Code Playgroud)

  • 如果你的函数太大,需要其中的区域,那么它们就太大了。 (7认同)
  • 这非常有用,因为我可以习惯将所有辅助函数放在函数底部的`#region Helpers`中,这样可以避免函数内的混乱,特别是避免主类中的混乱. (5认同)

Mih*_*dov 6

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

public class Foo // the class under test
{ 
    public int GetResult() 
    { 
        return 100 + GetLocal(); 
        int GetLocal () 
        { 
            return 42; 
        } 
    } 
}
Run Code Online (Sandbox Code Playgroud)

这是测试的外观:

[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); 
    } 
} 
Run Code Online (Sandbox Code Playgroud)

这是JustMock 文档的链接。

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


Ale*_*lex 6

我很好奇本地函数到底比 lambda 快多少,所以我写了一些基准测试:

[Benchmark]
public void TestLambda()
{
    Func<int, int> _Square = (x) => x * x;
    _Square(123);
}

[Benchmark]
public void TestLocalFunc()
{
    int _Square(int x) => x * x;
    _Square(123);
}
Run Code Online (Sandbox Code Playgroud)

结果令我震惊。

快了 4 个数量级!

|        Method |      Mean |     Error |    StdDev |    Median | Allocated |
|-------------- |----------:|----------:|----------:|----------:|----------:|
|    TestLambda | 1.4949 ns | 0.1997 ns | 0.0109 ns | 1.4898 ns |         - |
| TestLocalFunc | 0.0008 ns | 0.0237 ns | 0.0013 ns | 0.0000 ns |         - |

// * Warnings *
ZeroMeasurement
  BenchMark.TestLocalFunc: ShortRun -> The method duration is indistinguishable from the empty method duration
Run Code Online (Sandbox Code Playgroud)