実現したいこと
ループ形状の図の線上にいくつかの点を打ったものを扱うツールを開発しています。
適当な点を原点とし、原点から線を辿った距離を一次元の座標値として点の位置を表現しています。
順番に点を見ていったり、隣接する点を取得したりといった操作をするので、
循環リストのようなものを作れば便利では無いかと考えました。
また、foreachで回せて、どんな順で点を登録しても自動で順に並ぶとより都合が良いと思い、
SortedListを継承してCircularListという自作クラスを実装してみたのですが、
不便さの残る結果となってしまったのでアドバイス頂きたいです。
発生している問題・エラーメッセージ
下記にソースコードを示します。
ある程度思っていた動作はするのですが、
foreach文で回すとその都度Reset()関数を呼ばないとインデックス値が初期化されない、
SortedListベースであるため同じ座標値を登録すると重複で例外が発生してしまうなど微妙な結果となりました。
より良い循環リストの実装方法、もしくは循環リスト以外のより良いアプローチがあればご教示頂きたいです。
該当のソースコード
C#
1public class CircularList<TKey, TValue> : SortedList<TKey, TValue>, IEnumerator<KeyValuePair<TKey, TValue>> 2 { 3 /// <summary> 4 /// 循環する整列済みリスト 5 /// foreachでは原点から一周する(原点は初期値は0で変更可能) 6 /// </summary> 7 /// <returns></returns> 8 9 private int originIndex; 10 private int currentIndex; 11 private int numberOfCall; 12 private bool firstFlag; 13 14 public CircularList() 15 { 16 this.originIndex = 0; 17 this.currentIndex = -1; 18 this.numberOfCall = 0; 19 this.firstFlag = true; 20 } 21 22 public CircularList(IComparer<TKey> comparer) : base(comparer) 23 { 24 this.originIndex = 0; 25 this.currentIndex = -1; 26 this.numberOfCall = 0; 27 this.firstFlag = true; 28 } 29 30 public int OriginIndex 31 { 32 get { return this.originIndex; } 33 set { this.originIndex = value; } 34 } 35 36 public void SetOriginIndex() 37 { 38 this.originIndex = (this.currentIndex + 1) % this.Count; 39 } 40 public new IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() 41 { 42 return this; 43 } 44 public KeyValuePair<TKey, TValue> Current 45 { 46 get { return new KeyValuePair<TKey, TValue>(this.Keys[this.currentIndex], this.Values[this.currentIndex]); } 47 } 48 object System.Collections.IEnumerator.Current 49 { 50 get { return this; } 51 } 52 public bool MoveNext() 53 { 54 55 if (this.Count > 0) 56 { 57 this.currentIndex++; 58 this.currentIndex %= this.Count; 59 this.numberOfCall = 0; 60 } 61 62 if (this.currentIndex == this.originIndex && !firstFlag) 63 { 64 this.currentIndex--; 65 if (this.currentIndex < 0) 66 { 67 this.currentIndex = this.Count - 1; 68 } 69 firstFlag = true; 70 return false; 71 } 72 73 firstFlag = false; 74 75 return true; 76 } 77 public void Reset() 78 { 79 this.currentIndex = this.originIndex - 1; 80 if (this.currentIndex < 0) 81 { 82 this.currentIndex = this.Count - 1; 83 } 84 firstFlag = true; 85 } 86 public void Dispose() 87 { 88 89 } 90 91 public KeyValuePair<TKey, TValue> GetNextPair() 92 { 93 this.numberOfCall++; 94 int nextIndex = (this.currentIndex + this.numberOfCall) % this.Count; 95 return new KeyValuePair<TKey, TValue>(this.Keys[nextIndex], this.Values[nextIndex]); 96 } 97 98 public void ChangeKey(TKey oldKey, TKey newKey) 99 { 100 TValue value = this[oldKey]; 101 this.Remove(oldKey); 102 this[newKey] = value; 103 } 104 }
用途がよくわからないですが、Enumerator は別クラスにして GetEnumerator が呼ばれるたびにインスタンスを作成する必要があります。
IEnumerator<KeyValuePair<TKey, TValue>> インターフェイスを実装しておけばいいので、内部クラスにしても良いです。
.NET Framework のソースを見ると、Enumerator は struct になっていますね。
List<T>.GetEnumerator
https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,5d3accf5b217bdbf
List<T>.Enumerator
https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,9c3d580a8b7a8fe8
重複キーを登録できない問題に関しては、SortedList<TKey, TValue> の継承をあきらめるしかなさそうです。
また、複数の同一キーが存在する場合に、
(1) 追加はどの位置に行うのか(前につけるか後ろにつけるか)
(2) ChangeKey の振る舞いは?(同一キーすべて交換するのか、どれかひとつなのか、ひとつならどれを選択する?)
等のふるまいについて決定しておく必要があります。
「原点」はサンプルソースの`OriginIndex`(int)ですか?
TKey値を指定するのではなく、SortedListのインデックスのようですが、挿入のたびに原点値がどんどんずれていく気がします。
列挙の「起点」であって、「原点」は表現がちょっと違うような気がしますね。
