在 foreach 循环内手动增加枚举器

发布于 2025-01-05 02:05:44 字数 805 浏览 3 评论 0原文

我在 foreach 循环内有一个嵌套的 while 循环,我想在满足特定条件时无限期地推进枚举器。为此,我尝试将枚举器转换为 IEnumerator< T> (如果它在 foreach 循环中,则必须如此)然后在转换的对象上调用 MoveNext() ,但它给我一个错误,说我无法转换它。

无法通过引用转换、装箱转换、拆箱转换、包装转换或 null 类型转换将类型“System.DateTime”转换为 System.Collections.Generic.IEnumerator。

        foreach (DateTime time in times)
        {
            while (condition)
            {
                // perform action
                // move to next item
                (time as IEnumerator<DateTime>).MoveNext(); // will not let me do this
            }

            // code to execute after while condition is met
         }

手动递增的最佳方法是什么foreach 循环内的 IEnumerator?

编辑: 编辑以显示 while 循环之后有代码,一旦满足条件,我希望执行该代码,这就是为什么我想在 while 内手动递增然后打破它,而不是继续,这会让我回到顶部。如果这不可能,我相信最好的办法就是重新设计我的做法。

I have a nested while loop inside a foreach loop where I would like to advance the enumerator indefinitately while a certain condition is met. To do this I try casting the enumerator to IEnumerator< T > (which it must be if it is in a foreach loop) then calling MoveNext() on the casted object but it gives me an error saying I cannot convert it.

Cannot convert type 'System.DateTime' to System.Collections.Generic.IEnumerator via a reference conversion, boxing conversion, unboxing conversion, wrapping conversion, or null type conversion.

        foreach (DateTime time in times)
        {
            while (condition)
            {
                // perform action
                // move to next item
                (time as IEnumerator<DateTime>).MoveNext(); // will not let me do this
            }

            // code to execute after while condition is met
         }

What is the best way to manually increment the IEnumerator inside of the foreach loop?

EDIT:
Edited to show there is code after the while loop that I would like executed once the condition is met which is why I wanted to manually increment inside the while then break out of it as opposed to continue which would put me back at the top. If this isn't possible I believe the best thing is to redesign how I am doing it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

隔纱相望 2025-01-12 02:05:44

许多其他答案建议使用继续,这可能会很好地帮助您完成您需要做的事情。然而,为了展示手动移动枚举器,首先您必须拥有枚举器,这意味着将循环编写为while

using (var enumerator = times.GetEnumerator())
{
    DateTime time;
    while (enumerator.MoveNext()) 
    {
        time = enumerator.Current;
        // pre-condition code
        while (condition)
        {
            if (enumerator.MoveNext())
            {
                time = enumerator.Current;
                // condition code
            }
            else 
            {
                condition = false;
            }
        }
        // post-condition code
    }
}

根据您的评论:

如果 foreach 循环没有实现 IEnumerator 接口,它如何推进它?

在循环中,time 是一个DateTime。它不是需要实现接口或模式才能在循环中工作的对象。 times 是一系列 DateTime 值,它是必须实现可枚举模式的序列。这通常通过实现 IEnumerableIEnumerable 接口来实现,这些接口只需要 T GetEnumerator()object GetEnumerator( ) 方法。这些方法返回一个实现 IEnumeratorIEnumerator 的对象,其中定义了 bool MoveNext() 方法和 T 或 object Current 属性。但是 time 不能转换为 IEnumerator,因为它不是这样的东西,times 序列也不是。

Many of the other answers recommend using continue, which may very well help you do what you need to do. However, in the interests of showing manually moving the enumerator, first you must have the enumerator, and that means writing your loop as a while.

using (var enumerator = times.GetEnumerator())
{
    DateTime time;
    while (enumerator.MoveNext()) 
    {
        time = enumerator.Current;
        // pre-condition code
        while (condition)
        {
            if (enumerator.MoveNext())
            {
                time = enumerator.Current;
                // condition code
            }
            else 
            {
                condition = false;
            }
        }
        // post-condition code
    }
}

From your comments:

How can the foreach loop advance it if it doesn't implement the IEnumerator interface?

In your loop, time is a DateTime. It is not the object that needs to implement an interface or pattern to work in the loop. times is a sequence of DateTime values, it is the one that must implement the enumerable pattern. This is generally fulfilled by implementing the IEnumerable<T> and IEnumerable interfaces, which simply require T GetEnumerator() and object GetEnumerator() methods. The methods return an object implementing IEnumerator<T> and IEnumerator, which define a bool MoveNext() method and a T or object Current property. But time cannot be cast to IEnumerator, because it is no such thing, and neither is the times sequence.

や莫失莫忘 2025-01-12 02:05:44

您无法从 for 循环内部修改枚举器。语言不允许这样做。您需要使用 continue 语句才能前进到循环的下一次迭代。

但是,我不相信你的循环甚至需要继续。请继续阅读。

在代码上下文中,您需要将 while 转换为 if 以使 continue 引用 foreach 块。

foreach (DateTime time in times)       
{    
     if (condition) 
     {
             // perform action
             continue;
     }
     // code to execute if condition is not met    
}

但这样写很明显,以下等效变体仍然更简单

foreach (DateTime time in times)       
{    
     if (condition) 
     {
             // perform action
     }
     else
     {
            // code to execute if condition is not met   
     } 
}

这相当于您的伪代码,因为标记为 code toexecute after while condition is met 的部分针对每个条件执行是假的。

我对所有这些的假设是针对列表中的每个项目评估条件。

You cannot modify the enumerator from inside the for loop. The language does not permit this. You need to use the continue statement in order to advance to the next iteration of a loop.

However, I'm not convinced that your loop even needs a continue. Read on.

In the context of your code you would need to convert the while to an if in order to make the continue refer to the foreach block.

foreach (DateTime time in times)       
{    
     if (condition) 
     {
             // perform action
             continue;
     }
     // code to execute if condition is not met    
}

But written like this it is clear that the following equivalent variant is simpler still

foreach (DateTime time in times)       
{    
     if (condition) 
     {
             // perform action
     }
     else
     {
            // code to execute if condition is not met   
     } 
}

This is equivalent to your pseudo-code because the part marked code to execute after while condition is met is executed for each item for which condition is false.

My assumption in all of this is that condition is evaluated for each item in the list.

老街孤人 2025-01-12 02:05:44

也许您可以使用继续

Perhaps you can use continue?

弥枳 2025-01-12 02:05:44

您可以使用 continue 语句:
继续;

You would use the continue statement:
continue;

那小子欠揍 2025-01-12 02:05:44

这只是一个猜测,但听起来您想要做的是获取日期时间列表并跳过所有满足特定条件的日期时间,然后对列表的其余部分执行操作。如果这就是您想要做的,您可能需要 System.Linq 中的 SkipWhile() 之类的东西。例如,以下代码采用一系列日期时间并跳过截止日期之前的所有日期时间;然后它打印出剩余的日期时间:

var times = new List<DateTime>()
    {
        DateTime.Now.AddDays(1), DateTime.Now.AddDays(2), DateTime.Now.AddDays(3), DateTime.Now.AddDays(4)
    };

var cutoff = DateTime.Now.AddDays(2);

var timesAfterCutoff = times.SkipWhile(datetime => datetime.CompareTo(cutoff) < 1)
    .Select(datetime => datetime);

foreach (var dateTime in timesAfterCutoff)
{
    Console.WriteLine(dateTime);
}

Console.ReadLine();

这是您想要做的事情吗?

This is just a guess, but it sounds like what you're trying to do is take a list of datetimes and move past all of them which meet a certain criteria, then perform an action on the rest of the list. If that's what you're trying to do, you probably want something like SkipWhile() from System.Linq. For example, the following code takes a series of datetimes and skips past all of them which are before the cutoff date; then it prints out the remaining datetimes:

var times = new List<DateTime>()
    {
        DateTime.Now.AddDays(1), DateTime.Now.AddDays(2), DateTime.Now.AddDays(3), DateTime.Now.AddDays(4)
    };

var cutoff = DateTime.Now.AddDays(2);

var timesAfterCutoff = times.SkipWhile(datetime => datetime.CompareTo(cutoff) < 1)
    .Select(datetime => datetime);

foreach (var dateTime in timesAfterCutoff)
{
    Console.WriteLine(dateTime);
}

Console.ReadLine();

Is that the sort of thing you're trying to do?

夏天碎花小短裙 2025-01-12 02:05:44

我绝对不会容忍我要建议的内容,但您可以在原始 IEnumerable 周围创建一个包装器,将其转换为返回可用于导航底层枚举器的项目的内容。最终结果可能如下所示。

public static void Main(string[] args)
{
  IEnumerable<DateTime> times = GetTimes();
  foreach (var step in times.StepWise())
  {
    while (condition)
    { 
      step.MoveNext();
    }
    Console.WriteLine(step.Current);
  }
}

然后我们需要创建 StepWise 扩展方法。

public static class EnumerableExtension
{
    public static IEnumerable<Step<T>> StepWise<T>(this IEnumerable<T> instance)
    {
        using (IEnumerator<T> enumerator = instance.GetEnumerator())
        {
            while (enumerator.MoveNext())
            {
                yield return new Step<T>(enumerator);
            }
        }
    }

    public struct Step<T>
    {
        private IEnumerator<T> enumerator;

        public Step(IEnumerator<T> enumerator)
        {
            this.enumerator = enumerator;
        }

        public bool MoveNext()
        {
            return enumerator.MoveNext();
        }

        public T Current
        {
            get { return enumerator.Current; }
        }

    }
}

I definitely do not condone what I am about to suggest, but you can create a wrapper around the original IEnumerable to transform it into something that returns items which can be used to navigate the underlying the enumerator. The end result might look like the following.

public static void Main(string[] args)
{
  IEnumerable<DateTime> times = GetTimes();
  foreach (var step in times.StepWise())
  {
    while (condition)
    { 
      step.MoveNext();
    }
    Console.WriteLine(step.Current);
  }
}

Then we need to create our StepWise extension method.

public static class EnumerableExtension
{
    public static IEnumerable<Step<T>> StepWise<T>(this IEnumerable<T> instance)
    {
        using (IEnumerator<T> enumerator = instance.GetEnumerator())
        {
            while (enumerator.MoveNext())
            {
                yield return new Step<T>(enumerator);
            }
        }
    }

    public struct Step<T>
    {
        private IEnumerator<T> enumerator;

        public Step(IEnumerator<T> enumerator)
        {
            this.enumerator = enumerator;
        }

        public bool MoveNext()
        {
            return enumerator.MoveNext();
        }

        public T Current
        {
            get { return enumerator.Current; }
        }

    }
}
半透明的墙 2025-01-12 02:05:44

您可以使用 func 作为迭代器,并保留您在该委托中更改的状态,以便在每次迭代时进行评估。

public static IEnumerable<T> FunkyIEnumerable<T>(this Func<Tuple<bool, T>> nextOrNot)
    {
        while(true)
        {
           var result = nextOrNot();

            if(result.Item1)
                yield return result.Item2;

            else
                break;

        }

        yield break;

    }

    Func<Tuple<bool, int>> nextNumber = () => 
            Tuple.Create(SomeRemoteService.CanIContinueToSendNumbers(), 1);

    foreach(var justGonnaBeOne in nextNumber.FunkyIEnumerable())
            Console.Writeline(justGonnaBeOne.ToString());

You could use a func as your iterator and keep the state that you are changing in that delegate to be evaluated each iteration.

public static IEnumerable<T> FunkyIEnumerable<T>(this Func<Tuple<bool, T>> nextOrNot)
    {
        while(true)
        {
           var result = nextOrNot();

            if(result.Item1)
                yield return result.Item2;

            else
                break;

        }

        yield break;

    }

    Func<Tuple<bool, int>> nextNumber = () => 
            Tuple.Create(SomeRemoteService.CanIContinueToSendNumbers(), 1);

    foreach(var justGonnaBeOne in nextNumber.FunkyIEnumerable())
            Console.Writeline(justGonnaBeOne.ToString());
猫瑾少女 2025-01-12 02:05:44

尚未提及的一种替代方案是让枚举器返回一个包装器对象,除了被枚举的数据元素之外,该对象还允许访问其自身。例如:

struct ControllableEnumeratorItem<T>
{
  private ControllableEnumerator parent;
  public T Value {get {return parent.Value;}}
  public bool MoveNext() {return parent.MoveNext();}
  public ControllableEnumeratorItem(ControllableEnumerator newParent)
    {parent = newParent;}
}

想要允许在枚举期间以受控方式修改集合的数据结构也可以使用此方法(例如,通过包含“DeleteCurrentItem”、“AddBeforeCurrentItem”和“AddAfterCurrentItem”方法)。

One alternative not yet mentioned is to have an enumerator return a wrapper object which allows access to itself in addition to the data element being enumerated. For sample:

struct ControllableEnumeratorItem<T>
{
  private ControllableEnumerator parent;
  public T Value {get {return parent.Value;}}
  public bool MoveNext() {return parent.MoveNext();}
  public ControllableEnumeratorItem(ControllableEnumerator newParent)
    {parent = newParent;}
}

This approach could also be used by data structures that want to allow collections to be modified in controlled fashion during enumeration (e.g. by including "DeleteCurrentItem", "AddBeforeCurrentItem", and "AddAfterCurrentItem" methods).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文