1

我正在尝试打印从 2013 年 1 1 开始到 2015 年 1 1 结束的日期。

问题是 MoveNext 在 current 之前调用,因此它在 2013 2 1 开始打印。我的问题是 1).NET 中是否已经存在某种类型的 Range 类?我只知道 enumerable.range 不符合我的需要。2) 使用 abool hasStarted并在 MoveNext 中检查它是解决我的问题的最惯用的方法吗?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DateTest
{
    class Program
    {
        static void Main(string[] args)
        {
            foreach (var m in Range.Create(new DateTime(2013, 1, 1), new DateTime(2015, 1, 1), s => s.AddMonths(1)))
                Console.WriteLine(m);
        }
    }
    static class Range { public static Range<T> Create<T>(T s, T e, Func<T, T> inc) where T : IComparable<T> { return new Range<T>(s, e, inc); } }
    class Range<T> : IEnumerable<T>, IEnumerator<T> where T : IComparable<T> 
    {
        T start, pos, end;
        Func<T,T> inc;
        public Range(T s, T e, Func<T,T> inc) { pos=start= s; end = e; this.inc = inc; }

        public T Current
        {
            get { return pos; }
        }

        public void Dispose()
        {
            //throw new NotImplementedException();
        }

        object System.Collections.IEnumerator.Current
        {
            get { return pos; }
        }

        public bool MoveNext()
        {
            pos = inc(pos);
            return pos.CompareTo(end) < 0;
        }

        public void Reset()
        {
            pos = start;
        }

        public IEnumerator<T> GetEnumerator()
        {
            return this;
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this;
        }
    }
}
4

4 回答 4

3

我可能会重构代码,这样我就不必自己编写MoveNext()Current

class Range<T> : IEnumerable<T> where T : IComparable<T>
{
    // other stuff...

    public IEnumerator<T> GetEnumerator()
    {
        for (T val = start; val.CompareTo(end) < 0; val = inc(val))
            yield return val;
    }
}

有关其工作原理的更多信息,请参阅yield关键字文档

于 2013-08-08T18:53:50.397 回答
1

您可以使用迭代器:

class Range<T> : IEnumerable<T> where T : IComparable<T>
{
    T start, pos, end;
    Func<T, T> inc;
    public Range(T s, T e, Func<T, T> inc) { pos = start = s; end = e; this.inc = inc; }

    public IEnumerator<T> GetEnumerator()
    {
        T current = start;
        while (current.CompareTo(end) < 0)
        {
            yield return current;
            current = inc(current);
        }
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

您可以完全删除Range<T>该类并直接在静态Range.Create方法中实现迭代器。

于 2013-08-08T18:53:52.493 回答
0

正如我在这篇博文中所描述的那样,这是一种yield return无需创建自己的自定义 IEnumerable的情况:

public static IEnumerable<DateTime> DaysInRange(DateTime startDate, DateTime endDate)
{
   DateTime current = startDate;
    while (current <= endDate)
   {
       yield return current;
       current = current.AddDays(1);
   }
}
于 2013-08-08T18:55:11.833 回答
0

你可以使用 stock 方法得到你想要的(一个可枚举的日期列表)Enumerable.Range(),因此:

DateTime dtFrom = new DateTime(2013,1,1) ;
DateTime dtThru = new DateTime(2015,1,1) ;
DateTime[] dates = Enumerable.Range( 0 , (dtThru-dtFrom).Days )
                             .Select( x => dtFrom.AddDays(x) )
                             .ToArray()
                             ;

或者您可以推出自己的可枚举DateRange()方法。像这样的东西会起作用。首先是一个枚举来指定间隔的大小(有一个可用的,但你必须通过Microsoft.VisualBasic命名空间吸进整个 VB 运行时才能得到它。呜呜。):

public enum DateTimeInterval
{
  Day     = 1 ,
  Week    = 2 ,
  Month   = 3 ,
  Year    = 4 ,
}

然后是一个简单的可枚举方法:

public static IEnumerable<DateTime> DateRange( DateTime dateFrom , DateTime dateThru , DateTimeInterval interval , int increment )
{
  if ( increment < 1 ) throw new ArgumentOutOfRangeException("increment");

  for( DateTime date = dateFrom.Date ; date <= dateThru ; )
  {

    yield return date ;

    switch (interval )
    {
    case DateTimeInterval.Day   : date = date.AddDays(   increment   ) ; break ;
    case DateTimeInterval.Week  : date = date.AddDays(   increment*7 ) ; break ;
    case DateTimeInterval.Month : date = date.AddMonths( increment   ) ; break ;
    case DateTimeInterval.Year  : date = date.AddYears(  increment   ) ; break ;
    default                     : throw new ArgumentOutOfRangeException("interval") ;
    }

  }
}
于 2013-08-08T19:07:18.080 回答