public static IEnumerable<T> Pipe<T>(this IEnumerable<T> source, Action<T> action)
{
return _(); IEnumerable <T> _()
{
foreach (var element in source)
{
action(element);
yield return element;
}
}
}
我在Morelinq repo中找到了此代码,无法理解这一行:
return _(); IEnumerable <T> _()
此代码使用C#的相对新功能,称为 local函数。关于此功能的唯一不寻常的事情是其名称:开发人员对其使用了一个下划线。因此,该函数的名称是_
,因此调用看起来像:_()
现在,您知道return
语句返回调用名为_
的本地函数的结果,其余语法都会列入:
// This is a local function
IEnumerable <T> _() {
...
}
(OP对问题的评论)我们不能仅使用
foreach
使用CC_5?
您复制的方法包括另外两条行,这是了解区别的关键:
public static IEnumerable<T> Pipe<T>(this IEnumerable<T> source, Action<T> action)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (action == null) throw new ArgumentNullException(nameof(action));
return _(); IEnumerable <T> _()
{
foreach (var element in source)
{
action(element);
yield return element;
}
}
}
如果将foreach
与yield return
直接放入Pipe<T>
方法的主体中,则将延期参数检查,直到开始迭代IEnumerable<T>
结果为止。使用本地功能,即使在呼叫者从未迭代结果的情况下,您也会在调用Pipe<T>
时立即进行检查。
我是Morelinq的维护者。下面,我引用了拉动请求,该请求将为您提供本地功能的使用背后的背景:
该公关的目的是重构所有操作员的私人迭代方法(如有)作为局部功能(用C#7引入)。到目前为止,需要拆分才能急切地检查参数,当调用操作员方法时,而不是在第一次使用迭代器时(可能远离呼叫站点)。现在可以通过本地函数完成相同的分型,并具有简化代码的附加优势,例如:
- 父方法的参数处于范围中,因此实际迭代器实现方法的签名变得更简单,因为无需传递所有参数。
- 父方法的类型参数在范围中,因此不需要重复。
- 有一种少的顶级方法。
- 迭代器主体与使用它的实际公共操作员方法一起出现。
- 不需要对已经检查过的论点进行调试构建主张
要回答选择样式return _(); IEnumerable <T> _()
的选择,我将引用我在Pull Request#360中提供的基本原理:
将返回语句和本地函数声明放在一行上旨在弥补该语言不支持匿名迭代器的事实。该行上没有提供额外的信息或上下文,因此分开两个方面没有明确的清晰度,除了在样式中可能有些非正统。只要考虑多少不重要的东西:
- 本地迭代函数的名称,因此可以给它
_
的伪造名称。- 本地函数的返回类型是因为它具有外部方法的返回类型。
- 对本地函数的呼叫从未真正执行,因为迭代函数变得懒惰,因此自行突出调用甚至有些误导。
实际上返回的是带有其算法主体的迭代对象,因此将其全部放在一条线上的风格旨在使其看起来像这样。
造型的起源和发挥作用来自……
如果您足够斜视,您几乎可以相信C#7现在有匿名 迭代器…
&mdash;C#7中的匿名迭代器,几乎
另请参见#291中的一些示例。