简体   繁体   English

任何人都可以解释这个枚举器语法吗?

[英]Can anyone explain this enumerator syntax?

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

I've found this code in MoreLinq repo and can't understand this line: 我在MoreLinq repo中发现了这段代码并且无法理解这一行:

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

This code uses a relatively new feature of C#, called local function . 此代码使用C#的一个相对较新的功能,称为本地功能 The only unusual thing about this function is its name: developers used a single underscore for it. 关于这个函数唯一不寻常的是它的名字:开发人员使用了一个下划线。 Hence, the name of the function is _ , so the invocation looks like this: _() 因此,函数的名称是_ ,因此调用如下所示: _()

Now that you know that return statement returns the result of invoking a local function named _ , the rest of the syntax falls into place: 既然你知道return语句返回调用名为_的本地函数的结果,那么语法的其余部分就会出现:

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

(OP's comment on the question) Can't we just do foreach with yield return ? (OP对这个问题的评论)不能只是我们foreachyield return

The method that you copied included two additional lines, which are key to understanding the difference: 您复制的方法包括另外两行,这是理解差异的关键:

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

If you put foreach with yield return directly into the body of Pipe<T> method, argument checking would be deferred until you start iterating your IEnumerable<T> result. 如果将带有yield return foreach直接放入Pipe<T>方法的主体中,则会延迟参数检查,直到您开始迭代IEnumerable<T>结果。 With local function in place you would do the check as soon as Pipe<T> is called, even in situations when the caller never iterates the result. 使用本地函数,只要调用Pipe<T> ,就会立即进行检查,即使在调用者从不迭代结果的情况下也是如此。

I am the maintainer of MoreLINQ . 我是MoreLINQ的维护者 Below, I am quoting from a pull request that will give you the background behind the use of local functions: 下面,我引用一个pull请求 ,它将为您提供使用本地函数的背景:

The purpose of this PR is to refactor the private iterator methods of all operators (where possible) as local functions (introduced with C# 7) . 此PR的目的是将所有运算符的私有迭代器方法(如果可能)重构为本地函数(使用C#7引入) The split was needed up until now in order to have arguments checked eagerly, when the operator method is invoked and not when the iterator is used the first time (which may be far from the call site). 到目前为止需要进行拆分,以便在调用operator方法时急切地检查参数,而不是在第一次使用迭代器时(可能远离调用站点)。 The same split can now be done via a local function with the added benefit of code simplification like: 现在可以通过本地函数完成相同的拆分,并具有代码简化的附加好处,例如:

  • parameters of parent method are in scope so the signature of the actual iterator implementation method becomes simpler since there is no need to pass on all arguments. parent方法的参数在范围内,因此实际迭代器实现方法的签名变得更简单,因为不需要传递所有参数。
  • type parameters of parent method are in scope so don't need repeating. 父方法的类型参数在范围内,因此不需要重复。
  • there is one less top-level method to write. 有一种较少的顶级方法可以编写。
  • iterator body appears in-line to the actual public operator method that uses it. 迭代器主体与使用它的实际公共运算符方法串联。
  • there is no need for debug-build assertions for arguments that have already been checked 对于已经检查过的参数,不需要调试构建断言

To answer on the choice of the style return _(); IEnumerable <T> _() 回答关于样式return _(); IEnumerable <T> _() return _(); IEnumerable <T> _() , I'm going to quote the rationale I provided in pull request #360 to the project: return _(); IEnumerable <T> _() ,我将引用我在pull request#360中提供给项目的基本原理:

Putting the return statement and the local function declaration on a single line is designed to compensate for the fact that the language doesn't support anonymous iterators succinctly. 将return语句和本地函数声明放在一行是为了弥补语言不简洁地支持匿名迭代器的事实。 There is no extra information or context being provided on that line so there's no clarity gained by separating the two except that it might appear a little unorthodox in styling. 在该行上没有提供额外的信息或上下文,因此除了在样式上看起来有点不正统之外,将两者分开是没有明确的。 Just consider how many things are immaterial: 只考虑有多少事情是无关紧要的:

  • The name of the local iterator function so it is given the bogus name of _ . 本地迭代器函数的名称,因此给出了_的伪名。
  • The return type of the local function because it's redundant with the outer method's return type. 本地函数的返回类型,因为它与外部方法的返回类型是多余的。
  • The call to the local function never really executes because the iterator function becomes lazy so it's even somewhat misleading to highlight the call on its own. 对本地函数的调用永远不会真正执行,因为迭代器函数变得懒惰,所以甚至有点误导性地突出显示它自己的调用。

What's in fact being returned is an iterator object with the body of its algorithm and so the style of putting it all on a single line is designed to make it appear just as that. 事实上返回的是一个迭代器对象及其算法的主体,因此将它全部放在一行上的样式被设计成使它看起来就像那样。

The origin and play on the styling come from the idea that… 造型的起源和发挥来自......

If you squint hard enough, you can almost believe that C# 7 now has anonymous iterators 如果你足够眯眼,你几乎可以相信C#7现在有匿名迭代器 ......

Anonymous Iterators in C# 7, Almost - C#7中的匿名迭代器,差不多

See also some examples in #291 . 另请参阅#291中的一些示例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM