【问题标题】:Early execution of IEnumerableIEnumerable 的早期执行
【发布时间】:2019-02-18 13:44:04
【问题描述】:

所以我有两种方法都通过IEnumerable 集合执行迭代。

    public static IEnumerable<int> GetRange(int start, int count)
    {
        if (count < 0)
            throw new ArgumentOutOfRangeException(nameof(count));

        var end = start + count;

        for (int value = start; value < end; value++)
        {
            yield return value;
        }
    }


    public static IEnumerable<int> GetRangeFunction(int start, int count)
    {
        if (count < 0)
            throw new ArgumentOutOfRangeException(nameof(count));

        var end = start + count;
        return RangeEnumeration();

        //Using local function
        IEnumerable<int> RangeEnumeration()
        {
            for (var value = start; value < end; value++)
            {
                yield return value;
            }
        }
    }

我最近知道返回 IEnumerable 的方法在枚举之前不会执行。

所以我创建了两个代码块来调用每个方法

调用 GetRange

var iterator = GetRange(0, 10); // This does not start the execution of `GetRange` method. Instead, it waits till any item is requested
foreach(var item in iterator) //Now the GetRange method is called
{

}

GetRangeFunction

但是,在 GetRangeFunction 的情况下,该方法在创建迭代器时立即被调用。

var iterator = GetRangeFunction(0, 5);

为什么会出现这种行为?我也在想GetRangeFunction,在请求项目之前不会执行。

编辑

我的问题表述得不好,让我再解释一遍。

两个枚举器都通过 yield 一个接一个地返回项目。但是在 GetRange 的情况下,在对枚举数进行任何操作之前,不会执行任何语句 (not even checking count is less than zero)。但是,在 GetRangeFunction 的情况下,条件检查会在调用该方法以创建迭代器时执行。

【问题讨论】:

  • will not be executed until enumerated - 它将执行到返回枚举器对象的位置。对于普通的迭代器函数,这将是直接,您可能认为它“没有执行”。
  • 在编译期间是否只做出这些决定?如果我错了,请纠正我。
  • 在迭代器循环之前和产量返回之前添加Console.WriteLine。看看会发生什么。
  • @Steve,@GSerg 这实际上不是真的。使用yield的整个方法只会在枚举时执行
  • @RudreshaParameshappa 请参阅The implementation of iterators in C# and its consequences (part 1)。编译器将 整个 IEnumerable&lt;int&gt; GetRange 转换为状态机,因此在枚举之前不会执行其中的代码。但是IEnumerable&lt;int&gt; GetRangeFunction 根本没有转换为状态机,只有IEnumerable&lt;int&gt; RangeEnumeration() 是。

标签: c# .net .net-core ienumerable


【解决方案1】:

根据MSDN,本地函数可以让异常立即浮出水面。例如,考虑以下代码。

        static void Main()
        {            
            IEnumerable<int> ienum = GetNumber(50, 110);
            //below line will not execute if use GetNumberByLocalMethod
            Console.WriteLine("Retrieved enumerator...");

            foreach (var i in ienum)
            {
                Console.Write($"{i} ");
            }
        }

        public static IEnumerable<int> GetNumberByLocalMethod(int start, int end)
        {
            throw new Exception("deliberately exception");
            return InnerGetNumberByLocalMethod();
            IEnumerable<int> InnerGetNumberByLocalMethod()
            {
                for (int i = start; i <= end; i++)
                {                    
                        yield return i;
                }
            }
        }

        public static IEnumerable<int> GetNumber(int start, int end)
        {
            throw new Exception("deliberately exception");

            for (int i = start; i <= end; i++)
            {                
                    yield return i;
            }
        }

为保证本地方法版本能够快速获取异常,GetNumberByLocalMethod 版本会立即执行,不会等到第一次迭代。

【讨论】:

  • 我相信这只是因为本地方法被认为是一个单独的方法体,只有计算所有捕获的变量才能工作。由于GetNumberByLocalMethod 的外部方法体没有yield 语句,因此可以立即对其求值。
  • @ChaosPandion 是的,你是对的,我也这么认为。
猜你喜欢
  • 1970-01-01
  • 2011-07-12
  • 2012-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-04
  • 1970-01-01
相关资源
最近更新 更多