【问题标题】:Can anyone explain this enumerator syntax?谁能解释这个枚举器语法?
【发布时间】:2017-12-27 10:59:14
【问题描述】:
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 存储库中找到了这段代码,但无法理解这一行:

return _(); IEnumerable <T> _()

【问题讨论】:

  • 将其分成两行(分号后),然后将所有出现的_ 替换为enumerate
  • @KevinGosse - 如果您没有跟上 C# 7 (Local functions) 的速度,即使在这样做之后也可能不会立即清楚。
  • 谢谢!但那有什么意义呢?我们不能只用foreachyield return 做吗?

标签: c# .net morelinq


【解决方案1】:

这段代码使用了 C# 的一个相对较新的特性,称为 local function。这个函数唯一不寻常的是它的名字:开发人员使用了一个下划线。因此,函数的名称是_,所以调用看起来像这样:_()

既然您知道return 语句返回调用名为_ 的本地函数的结果,那么剩下的语法就到位了:

// This is a local function
IEnumerable <T> _() {
    ...
}

(OP 对问题的评论)我们不能只用yield returnforeach 吗?

您复制的方法包括另外两行,这是理解差异的关键:

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;
        }
    }
}

如果您将foreachyield return 直接放入Pipe&lt;T&gt; 方法的主体中,则参数检查将被推迟到您开始迭代您的IEnumerable&lt;T&gt; 结果。有了本地函数,您将在调用 Pipe&lt;T&gt; 时立即进行检查,即使在调用者从不迭代结果的情况下也是如此。

【讨论】:

  • 不错的功能。如果在return之后定义一个局部函数是否有效?
  • @FelipeOriani 这对我也很感兴趣,返回后是否有效?
  • @FelipeOriani 是的。事实上,微软的例子展示了一个使用后定义的本地函数。
  • @FelipeOriani, @Aria 局部函数仅在函数内部可见。事实上,本地函数本身可以包含本地函数:) 如果方法是static,则本地函数也隐含为static
【解决方案2】:

我是MoreLINQ 的维护者。下面,我引用了a pull request,它将为您提供使用本地函数的背景:

此 PR 的目的是将所有运算符的私有迭代器方法(如果可能)重构为 local functions (introduced with C# 7)。到目前为止,需要拆分以便在调用运算符方法时而不是在第一次使用迭代器时(可能远离调用站点)时急切地检查参数。现在可以通过本地函数完成相同的拆分,并具有代码简化的额外好处,例如:

  • 父方法的参数在范围内,因此实际迭代器实现方法的签名变得更简单,因为不需要传递所有参数。
  • 父方法的类型参数在范围内,不需要重复。
  • 要编写的顶级方法少了一种。
  • 迭代器主体显示在使用它的实际公共运算符方法中。
  • 对于已检查的参数,无需调试生成断言

为了回答return _(); IEnumerable &lt;T&gt; _() 样式的选择,我将引用我在pull request #360 中为项目提供的基本原理:

将 return 语句和本地函数声明放在一行中旨在弥补语言不简洁地支持 匿名 迭代器的事实。该行没有提供额外的信息或上下文,因此将两者分开并没有获得明确性,除了它可能在样式上看起来有点不正统。想想有多少事情是无关紧要的:

  • 本地迭代器函数的名称,因此它被赋予了虚假名称_
  • 本地函数的返回类型,因为它与外部方法的返回类型冗余。
  • 对本地函数的调用永远不会真正执行,因为迭代器函数变得懒惰,因此单独突出显示调用甚至有些误导。

实际上返回的是一个带有算法主体的迭代器对象,因此将其全部放在一行中的风格旨在使其看起来像那样。

样式的起源和作用来自于……

如果你眯得够深,你几乎可以相信 C# 7 现在有 anonymous iterators...

——Anonymous Iterators in C# 7, Almost

另请参阅#291 中的一些示例。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-08
    • 2012-02-12
    • 1970-01-01
    • 1970-01-01
    • 2016-04-10
    • 2010-10-19
    相关资源
    最近更新 更多