Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么迭代器在异常情况下的行为与LINQ枚举不同?_C#_Linq_Iterator_Enumeration - Fatal编程技术网

C# 为什么迭代器在异常情况下的行为与LINQ枚举不同?

C# 为什么迭代器在异常情况下的行为与LINQ枚举不同?,c#,linq,iterator,enumeration,C#,Linq,Iterator,Enumeration,我正在研究这些方法的内部机制,我注意到迭代器得到的结果与LINQ方法得到的IEnumerator在行为上有一个奇怪的差异。如果枚举期间发生异常,则: LINQ枚举器保持活动状态。它跳过一个项目,但继续产生更多 迭代器枚举器完成。它不生产更多的产品 例如。顽固地枚举IEnumerator,直到它完成: private static void StubbornEnumeration(IEnumerator<int> enumerator) { using (enumerator)

我正在研究这些方法的内部机制,我注意到迭代器得到的结果与LINQ方法得到的
IEnumerator
在行为上有一个奇怪的差异。如果枚举期间发生异常,则:

  • LINQ枚举器保持活动状态。它跳过一个项目,但继续产生更多
  • 迭代器枚举器完成。它不生产更多的产品 例如。顽固地枚举
    IEnumerator
    ,直到它完成:

    private static void StubbornEnumeration(IEnumerator<int> enumerator)
    {
        using (enumerator)
        {
            while (true)
            {
                try
                {
                    while (enumerator.MoveNext())
                    {
                        Console.WriteLine(enumerator.Current);
                    }
                    Console.WriteLine("Finished");
                    return;
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Exception: {ex.Message}");
                }
            }
        }
    }
    
    输出:

    1
    2
    例外:哎呀
    4
    5
    例外:哎呀
    7
    8
    例外:哎呀
    10
    完成

    现在,让我们使用迭代器来尝试同样的方法,迭代器每3项抛出一次:

    var linqEnumerable = Enumerable.Range(1, 10).Select(i =>
    {
        if (i % 3 == 0) throw new Exception("Oops!");
        return i;
    });
    StubbornEnumeration(linqEnumerable.GetEnumerator());
    
    StubbornEnumeration(MyIterator().GetEnumerator());
    
    static IEnumerable<int> MyIterator()
    {
        for (int i = 1; i <= 10; i++)
        {
            if (i % 3 == 0) throw new Exception("Oops!");
            yield return i;
        }
    }
    
    枚举(MyIterator().GetEnumerator());
    静态IEnumerable MyIterator()
    {
    
    对于(int i=1;i第二个示例中的
    yield
    语法起作用。使用它时,编译器会生成一个状态机,该状态机在后台管理一个实枚举数。抛出异常会退出函数,从而终止状态机。

    第二个示例中的
    yield
    语法使差异。当您使用它时,编译器会生成一个状态机来管理一个真正的枚举器。抛出异常会退出函数,从而终止状态机。

    有没有办法重写我的迭代器,使其行为类似于LINQ枚举器?是的,您可以构建自己的
    IEnumerable实现
    IEnumerator
    (这正是LINQ在引擎盖下所做的),允许异常从
    MoveNext()中冒泡出来
    而不将其内部状态设置为finished。您是否有任何解释,为什么迭代器的实现方式与日常开发人员手动编写
    IEnumerable
    /
    IEnumerator
    对的方式不一致?这两种方式都是基于上下文的。在LINQ的情况下,我们抛出了例外在lambda内部,每次迭代显式运行一次。在
    yield
    的情况下,如果我们抛出一个异常,但以某种方式继续运行该函数而不是离开它,那么在这里,试图理解和解释该行为会有很多问题!我做了这个实验。我复制了我的迭代器粘贴到,获取生成的代码,修复了非法变量名以使其编译,并将其用作
    顽固枚举
    的参数。它的行为类似于迭代器。是否有方法重写我的迭代器,使其行为类似于LINQ枚举?是的,您可以构建自己的
    IEnumerable
    IEnumerator
    (这正是LINQ在幕后所做的)允许异常从
    MoveNext()冒泡出来
    而不将其内部状态设置为finished。您是否有任何解释,为什么迭代器的实现方式与日常开发人员手动编写
    IEnumerable
    /
    IEnumerator
    对的方式不一致?这两种方式都是基于上下文的。在LINQ的情况下,我们抛出了例外在lambda内部,每次迭代显式运行一次。在
    yield
    的情况下,如果我们抛出一个异常,但以某种方式继续运行该函数而不是离开它,那么在这里,试图理解和解释该行为会有很多问题!我做了这个实验。我复制了我的迭代器粘贴到,获取生成的代码,修复了非法变量名以使其进行编译,并将其用作
    顽固枚举
    的参数。它的行为类似于迭代器。第一个代码示例应该很清楚。由于捕获了每个异常,外部循环不会中断。带有
    yield return
    的示例可能看起来有点奇怪,但编译器确实如此Dennis_E相同的方法
    顽固枚举
    用于枚举两个枚举数,LINQ枚举数和迭代器枚举数。结果是不同的。老实说,我没想到会存在这种差异。第一个代码示例应该很清楚。因为您捕获了所有异常,外部循环没有中断。
    yield return
    的示例可能看起来有点奇怪,但编译器在幕后做了很多工作,使其能够像这样工作。@Dennis_E使用相同的方法
    顽固枚举
    来枚举两个枚举器,LINQ枚举器和迭代器枚举器。结果不同。我没有体验到老实说,这种差异并不存在。