本地函数与 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<int, int, int> f = (x, y) => 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的主要内容,如果未能解决你的问题,请参考以下文章