语法使用yield return </t>发出IEnumerable <t>方法

时间:2011-11-29 13:14:47

标签: c# yield-return

这是我的方法:

static IEnumerable<DateTime> GetMonths(DateTime from, DateTime to)
{
    // if logs is not uptodate
    TimeSpan logsMissingTimespan = to - from;

    if (logsMissingTimespan != new TimeSpan(0))
    {
        return GetMonthsBetweenTwoDates(from, to);
    }

    return null; // Why this line ?
}

private static IEnumerable<DateTime> GetMonthsBetweenTwoDates(DateTime from, DateTime to)
{

    DateTime date = from;
    DateTime lastDate = DateTime.MaxValue;

    while (date < to)
    {
        if (lastDate.Month != date.Month)
        {
            lastDate = date;
            yield return lastDate;
        }
        date = date.AddDays(1);
    }
}

它工作正常,但我想我可以写一些像这样清洁的东西:

static IEnumerable<DateTime> GetMonths(DateTime from, DateTime to)
{
    TimeSpan logsMissingTimespan = to - from;

    if (logsMissingTimespan == new TimeSpan(0))
    {
        yield break;
    }

    return GetMonthsBetweenTwoDates(from, to);
}

但我有一条错误消息:

  

无法从迭代器返回值。使用yield return语句返回一个值,或者使用yield break来结束迭代。

为什么我应该有return null以及正确的语法?

编辑:

所以,正确的方法是使用Enumerable.Empty:

static IEnumerable<DateTime> GetMonths(DateTime from, DateTime to)
{
    // if logs is not uptodate
    TimeSpan logsMissingTimespan = to - from;

    if (logsMissingTimespan != new TimeSpan(0))
    {
        return GetMonthsBetweenTwoDates(from, to);
    }

    return Enumerable.Empty<DateTime>();
}

4 个答案:

答案 0 :(得分:5)

因为您使用了单词yield,所以它现在希望该方法一次产生一个元素。它必须仅使用yeild returnyield break每次迭代返回一个元素。

您应该使用Enumerable.Empty<DateTime>();代替yield break

答案 1 :(得分:3)

一个方法是用迭代器块实现的,或者不是 - 所以所有都是yield returnyield break,或者都不是。

但是,你 不需要做任何特别的事情。您的原始GetMonthsBetweenTwoDates已经在to == from处工作,因为它永远不会进入while循环。

另一方面,您对lastDate的使用看起来很可疑 - 特别是,如果from碰巧与{{1}同一个月,它看起来会有所不同}}。

答案 2 :(得分:3)

前两个示例的形式会产生不同类型的输出。

如果满足条件,您的第一个示例将直接返回IEnumerable<T>,如果不满足,则返回空引用。您的第二个示例始终返回IEnumerable<T>,但条件确定它是否包含任何元素。

第二个例子是使用迭代器块完成的。 C#编译器使用yield语法将您编写的函数转换为实现IEnumerable<T>的自定义(隐藏)类型和实现IEnumerator<T>的类型。这些类型实现了必要的状态机,以实现(希望)您放入函数的逻辑。因此,你不能混淆范式;您必须从函数返回IEnumerable<T>的实例(而不是在任何地方使用yield),或者必须通过yield返回所有内容。

如果你所关心的只是你要返回一个空引用,那么你可以通过返回Enumerable.Empty<DateTime>而不是null来使方法在语义上相同。

答案 3 :(得分:0)

在第一次检查时,您不需要屈服中断来结束迭代。

if (logsMissingTimespan == new TimeSpan(0))
{
    return null;
}    
return GetMonthsBetweenTwoDates(from, to);