【问题标题】:Iterating an array with foreach backwards without using extension method在不使用扩展方法的情况下使用 foreach 向后迭代数组
【发布时间】:2014-11-22 10:14:03
【问题描述】:

在谷歌搜索后,我发现了这个讨论:

Possible to iterate backwards through a foreach?

但在答案中使用了扩展方法 .Reverse()。使用反向,对象列表,即。字符串列表,会先颠倒过来,我理解的foreach不会颠倒列表吗?如果我得到列表“Cat”,“Dog”,并使用 .Reverse() -方法,列表将是“Dog”,“Cat”,foreach 从 0 元素开始直到 lenght-1 -元素,这就是我不是在寻找。我想知道,如果有任何方法可以反转 foreach 迭代顺序,从 lenght-1 开始到 0。

【问题讨论】:

  • 如果你真的有Array,为什么还要关心foreach?好老的for 会做得很好,而且可能比其他任何方式都快。
  • 你为什么不写你自己的(扩展)方法?您想要的东西在标准库中并不常见且通常。

标签: c# arrays iteration


【解决方案1】:

如果有任何方法可以反转 foreach 迭代顺序,从长度 1 开始到 0

不适用于List<T>GetEnumerator() 的实现返回一个从头到尾枚举的枚举器——没有办法覆盖它。

使用 custom 集合,那么您只需要使用 可以 倒退的不同枚举器,但无法覆盖 List<T> 的实现用途。

【讨论】:

    【解决方案2】:

    Reverse 方法将 copy the list first:

    public static IEnumerable<TSource> Reverse<TSource>(this IEnumerable<TSource> source) {
        if (source == null) throw Error.ArgumentNull("source"); 
        return ReverseIterator<TSource>(source);
    }
    
    static IEnumerable<TSource> ReverseIterator<TSource>(IEnumerable<TSource> source) { 
        Buffer<TSource> buffer = new Buffer<TSource>(source);
        for (int i = buffer.count - 1; i >= 0; i--) yield return buffer.items[i]; 
    } 
    

    但是你可以自己做一个扩展方法:

    public static IEnumerable<TSource> Backwards<TSource>(this IList<TSource> source) {
        for (var i = source.Count - 1; i >= 0; --i)
            yield return source[i];
    }
    

    然后像这样使用它:

    foreach (var item in array.Backwards())
        Console.WriteLine(item); // Or whatever else
    

    或者,当然,你也可以这样做:

    for (var i = array.Length - 1; i >= 0; --i)
        Console.WriteLine(array[i]); // Or whatever else
    

    【讨论】:

    • @DStanley 这很不幸,但即使在这种情况下它确实复制了一份。请参阅我的答案中的源代码。还有Buffer&lt;T&gt;copies the list
    【解决方案3】:

    您可以实现一个向后迭代列表的枚举器。这样您就可以使用foreach,而无需更改原始列表或创建它的副本。

    public class ReverseEnumerator<T> : IEnumerator<T> {
    
      private IList<T> _list;
      private int _index;
      private T _current;
    
      public ReverseEnumerator(IList<T> list) {
        _list = list;
        Reset();
      }
    
      public IEnumerator<T> GetEnumerator() {
        return this;
      }
    
      public T Current {
        get {
          if (_index < 0 && _index >= _list.Count) throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
          return _current;
        }
      }
    
      public void Dispose() { }
    
      object IEnumerator.Current { get { return Current; } }
    
      public bool MoveNext() {
        bool ok = --_index >= 0;
        if (ok) _current = _list[_index];
        return ok;
      }
    
      public void Reset() {
        _index = _list.Count;
      }
    
    }
    

    使用示例:

    int[] a = { 1, 2, 3, 4, 5 };
    
    foreach (int x in new ReverseEnumerator<int>(a)) {
      Console.WriteLine(x);
    }
    

    【讨论】:

    • 您可能希望将其作为ICollection&lt;T&gt;(如果这是.NET 4.5,甚至是IReadOnlyCollection&lt;T&gt;)而不是IList&lt;T&gt;,它将与更多东西兼容并且您仍然拥有访问索引器。
    • @ScottChamberlain:您如何通过索引访问ICollection&lt;T&gt; 中的项目?
    • 嗯,我会为 ICollection 暴露 Index 属性这一事实投入资金。对不起。
    猜你喜欢
    • 1970-01-01
    • 2020-10-14
    • 2021-12-29
    • 2011-11-29
    • 1970-01-01
    • 1970-01-01
    • 2020-02-26
    • 2011-07-05
    相关资源
    最近更新 更多