【问题标题】:Circular lists in C#C# 中的循环列表
【发布时间】:2015-11-18 13:53:07
【问题描述】:

我对 C# 不是很有经验。我正在尝试建立循环列表,我已经这样做了:

public List<string> items = new List<string> {"one", "two", "three"};
private int index = 0;

void nextItem() {
    if (index < items.Count - 1)
        index += 1;
    else
        index = 0;

    setItem();
}

void previousItem() {
    if (index > 0)
        index -= 1;
    else
        index = items.Count - 1;

    setItem();
}

void Update() {
         if (Input.GetKeyDown(KeyCode.RightArrow)) nextItem();
    else if (Input.GetKeyDown(KeyCode.LeftArrow))  previousItem();
}

但现在我想知道:我是在重新发明轮子吗? C# 是否已经为此提供了适当的数据结构?

编辑:如果需要一些上下文。我有一个游戏菜单,其中显示了一系列项目,我希望当我按“下一步”并在最后一个时,再次显示第一个项目。

【问题讨论】:

  • 您的列表不是循环的,只是索引它的方法。你只是想通过一个环绕的列表来索引,还是你真的想要一个循环缓冲区?
  • 在这里使用队列要好得多。
  • 我有一个游戏,我在其中显示菜单中的项目列表。当我到达最后一项时,我想再次显示第一项。
  • @M.kazemAkhgary 真的吗? previousItempreviousItem?
  • 可以将下一个索引的计算简化为index = (index+1)%items.Count;。同理,前面索引的计算可以简化为index = (items.Count+index-1)%items.Count;(虽然“简化”在这个语境中可能是一个相对术语……)

标签: c#


【解决方案1】:

使用% (remainder) operator,您的代码变得非常简单:

void nextItem() {
    index++; // increment index
    index %= items.Count; // clip index (turns to 0 if index == items.Count)
    // as a one-liner:
    /* index = (index + 1) % items.Count; */

    setItem();
}

void previousItem() {
    index--; // decrement index
    if(index < 0) {
        index = items.Count - 1; // clip index (sadly, % cannot be used here, because it is NOT a modulus operator)
    }
    // or above code as a one-liner:
    /* index = (items.Count+index-1)%items.Count; */ // (credits to Matthew Watson)

    setItem();
}

【讨论】:

    【解决方案2】:

    你也不能编写自己的循环列表

    public class CircularList<T> : List<T>, IEnumerable<T>
    {
        public new IEnumerator<T> GetEnumerator()
        {
            return new CircularEnumerator<T>(this);
        }
    
        IEnumerator IEnumerable.GetEnumerator()
        {
            return new CircularEnumerator<T>(this);
        }
    }
    
    class CircularEnumerator<T> : IEnumerator<T>
    {
        private readonly List<T> list;
        int i = 0;
    
        public CircularEnumerator(List<T> list){
            this.list = list;
        }
    
        public T Current => list[i];
    
        object IEnumerator.Current => this;
    
        public void Dispose()
        {
            
        }
    
        public bool MoveNext()
        {
            i = (i + 1) % list.Count;
            return true;
        }
    
        public void Reset()
        {
            i = 0;
        }
    }
    

    【讨论】:

    • 为什么CircularList需要实现IEnumerable?
    • 对,这不是必须的,但它必须覆盖 List 类的枚举器功能才能实现循环行为——在我看来,实现 IEnumerable 使这一点更加清晰
    • 那么如果你把它扔到一个foreach循环或其他IEnumerable循环中,它会无限枚举吗?这既酷又令人讨厌。需要像圈数检测或圈数截止阈值作为参数。
    【解决方案3】:
        public class CircularList<T> : List<T>
        {
            private int Index;
    
            public CircularList() : this(0) { }
    
            public CircularList(int index)
            {
                if (index < 0 || index >= Count)
                    throw new Exception(string.Format("Index must between {0} and {1}", 0, Count));
    
                Index = index;
            }
    
            public T Current()
            {
                return this[Index];
            }
    
            public T Next()
            {
                Index++;
                Index %= Count;
    
                return this[Index];
            }
    
            public T Previous()
            {
                Index--;
                if (Index < 0)
                    Index = Count - 1;
    
                return this[Index];
            }
    
            public void Reset()
            {
                Index = 0;
            }
    
            public void MoveToEnd()
            {
                Index = Count - 1;
            }
    
        }
    

    【讨论】:

      猜你喜欢
      • 2021-12-20
      • 1970-01-01
      • 2011-08-03
      • 1970-01-01
      • 2011-08-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-01
      相关资源
      最近更新 更多