这是我的方法:
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>();
}
因为您使用了单词yield
,它现在期望该方法每次生成一个元素。每次迭代只能使用yeild return
或yield break
返回一个元素。
你应该用Enumerable.Empty<DateTime>();
而不是yield break
。
前两个示例的形式产生不同类型的输出。
如果满足条件,则直接返回IEnumerable<T>
,如果不满足则返回空引用。第二个例子总是返回一个IEnumerable<T>
,但是条件决定了它是否有任何元素。
yield
语法将您编写的函数转换为实现IEnumerable<T>
的自定义(隐藏)类型和实现IEnumerator<T>
的类型。这些类型实现了必要的状态机,以实现(希望如此)您放入函数中的逻辑。正因为如此,你不能混合范式;您必须从函数返回IEnumerable<T>
的实例(并且在任何地方都不使用yield
),或者必须通过yield
返回所有内容。
如果你所关心的是你返回一个空引用的事实,你可以通过返回Enumerable.Empty<DateTime>
而不是null
使方法在语义上相同。
方法要么是用迭代器块实现的,要么不是——所以要么所有都是按照yield return
和yield break
来实现的,要么都不是。
GetMonthsBetweenTwoDates
已经在to == from
的地方工作了,因为它永远不会进入while循环。
另一方面,你对lastDate
的使用对我来说很可疑——特别是,如果from
碰巧和DateTime.MaxValue
在同一个月,它看起来会做一些不同的事情。
在第一次检查时不需要yield break来结束迭代。
if (logsMissingTimespan == new TimeSpan(0))
{
return null;
}
return GetMonthsBetweenTwoDates(from, to);