使用委托movenext / increase可枚举的范围

时间:2013-08-08 18:44:17

标签: .net

我试图打印2013年开始的日期1 1结束2015 1 1独家。

问题是在当前之前调用MoveNext所以它在2013年开始打印2 1.我的问题是1).NET中是否存在某种类型的Range类?我只知道enumerable.range并不接近我的需要。 2)使用bool 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 个答案:

答案 0 :(得分: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 keyword documentation

答案 1 :(得分: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方法中实现迭代器。

答案 2 :(得分:0)

这是yield return无法创建自己的自定义IEnumerable的情况,正如我在this blog post中所描述的那样:

public static IEnumerable<DateTime> DaysInRange(DateTime startDate, DateTime endDate)
{
   DateTime current = startDate;
    while (current <= endDate)
   {
       yield return current;
       current = current.AddDays(1);
   }
}

答案 3 :(得分: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运行时才能得到它.Woof。):

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") ;
    }

  }
}