Yield return 和异常处理

8

我刚刚遇到了一个使用yield return方法的案例,但它没有抛出我所预期的ArgumentException异常。我在这里用最简单的类重构了这个案例:

class Program
{
    static void Main(string[] args)
    {
        try
        {
            var listA = FooA(count: 0);
            Console.WriteLine("A did not throw exception!");
        }
        catch (ArgumentException)
        {
            Console.WriteLine("A threw exception!");
        }

        try
        {
            var listB = FooB(count: 0);
            Console.WriteLine("B did not throw exception!");
        }
        catch (ArgumentException)
        {
            Console.WriteLine("B threw exception!");
        }

        Console.ReadLine();
    }

    private static IEnumerable<int> FooA(int count)
    {
        if(count == 0)
            throw new ArgumentException("Count must be above 0");
        var list = new List<int>();
        for (var i = 0; i < count; i++)
        {
            list.Add(i);
        }
        return list;
    }

    private static IEnumerable<int> FooB(int count)
    {
        if (count == 0)
            throw new ArgumentException("Count must be above 0");
        for (var i = 0; i < count; i++)
        {
            yield return i;
        }
    }
}

输出:

A threw exception!
B did not throw exception!

有人能否解释一下为什么FooB不会抛出异常,而FooA会抛出异常呢?

你有更多的信息吗? - Nir Schwartz
2
只有在开始迭代之前,FooB才不会抛出异常。这是一个常见的陷阱。这是因为yield return语句将该方法转换为一个类。 - Dennis_E
2
通常情况下,您会将yield方法的实现拆分为单独的私有方法,以便错误处理发生在调用站点。 - juharr
因为FooB()从未被调用。 - Anwar Ul-haq
1个回答

12

这是因为FooB从未被评估。

当您调用方法时,该方法会立即被调用。当您使用yield并返回一个可枚举对象时,只有在需要使用返回值的值时才会调用该方法,每次仅调用一个项目; 这是yield的好处。

因此,如果添加了使用该值的内容

try
{
    var listB = FooB(count: 0);
    Console.WriteLine(listB.First()); // use the IEnumerable returned
    Console.WriteLine("B did not throw exception!");
}
catch (ArgumentException)
{
    Console.WriteLine("B threw exception!");
}

您将看到您期望的结果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接