语法问题IEnumerable<方法使用yield return

本文关键字:yield return 方法 问题 IEnumerable 语法 | 更新日期: 2023-09-27 18:16:10

这是我的方法:

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。空:
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>();
}

语法问题IEnumerable<方法使用yield return

因为您使用了单词yield,它现在期望该方法每次生成一个元素。每次迭代只能使用yeild returnyield break返回一个元素。

你应该用Enumerable.Empty<DateTime>();而不是yield break

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

如果满足条件,则直接返回IEnumerable<T>,如果不满足则返回空引用。第二个例子总是返回一个IEnumerable<T>,但是条件决定了它是否有任何元素。

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

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

方法要么是用迭代器块实现的,要么不是——所以要么所有都是按照yield returnyield break来实现的,要么都不是。

然而,不需要做任何特别的事情。你原来的GetMonthsBetweenTwoDates已经在to == from的地方工作了,因为它永远不会进入while循环。

另一方面,你对lastDate的使用对我来说很可疑——特别是,如果from碰巧和DateTime.MaxValue在同一个月,它看起来会做一些不同的事情。

在第一次检查时不需要yield break来结束迭代。

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