我理解Lambda是
匿名
函数,而局部函数不是,但我无法想出一个现实世界的场景,局部函数优于Lambda表达式。非常感谢您提供任何示例。谢谢。
这在C#设计会议笔记中被Mads Torgersen解释过,该会议首次讨论了局部函数:
你需要一个辅助函数。你只从单个函数内部使用它,它可能使用在包含函数中范围内的变量和类型参数。另一方面,与Lambda不同,您不需要将其作为第一类对象,因此您不关心为其分配委托类型并分配实际委托对象。您还可以将其实现为递归或通用函数,或者将其实现为迭代器。
更详细地说,其优点如下:
性能。
创建Lambda时必须创建委托,但在本例中,这是不必要的分配。局部函数只是函数,不需要委托。
此外,局部函数更有效地捕获局部变量:Lambda通常将变量捕获到类中,而局部函数可以使用结构体(使用ref
传递),这样可以避免分配。
这也意味着调用局部函数更便宜,并且它们可以内联,进一步提高性能。
局部函数可以是递归的。
Lambda也可以是递归的,但它需要笨拙的代码,其中您首先将null
分配给委托变量,然后再分配Lambda。局部函数可以自然地递归(包括相互递归)。
局部函数可以是通用的。
Lambda不能是通用的,因为它们必须分配给具有具体类型的变量(该类型可以使用来自外部作用域的通用变量,但这不是同一件事)。
局部函数可以实现为迭代器。
Lambda不能使用yield return
(和yield break
)关键字来实现返回IEnumerable<T>
的函数。局部函数可以。
局部函数看起来更好。
这在上面的引用中没有提到,可能只是我的个人偏见,但我认为普通函数语法比将Lambda分配给委托变量要好看。局部函数也更简洁。
比较:
int add(int x, int y) => x + y;
Func<int, int, int> add = (x, y) => x + y;
Func<int, int, int> f = (x, y) => x + y; f(arg1:1, arg2:1);
。 - svick除了 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;
}
#region Helpers
中,以避免在该函数内产生混乱,尤其是避免在主类中出现混乱。 - AustinWBryanpublic 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 的文档链接。
声明:我是 JustMock 的开发人员之一。
我很好奇本地函数比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);
}
结果让我惊呆了。
| 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
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;
}
}
您可以看到以下提到的优点,这里有一个示例实现。希望这有助于解释其好处。