1 | using System;
|
---|
2 | using System.Collections.Generic;
|
---|
3 |
|
---|
4 | // collection class with the following operations:
|
---|
5 | // * add object
|
---|
6 | // * iterate collection using foreach statement
|
---|
7 | // * remove current object from within iteration
|
---|
8 |
|
---|
9 | // when removing objects while iterating, the following conditions are assured:
|
---|
10 | // * no elements that are still in the collection are left out
|
---|
11 | // * no elements that are no longer in the collection are iterated
|
---|
12 | // * no element is iterated twice
|
---|
13 | // * in case of nested iterations of the collection, all this is true for each of them
|
---|
14 |
|
---|
15 | // calling GetEnumerator explicitely is not recommended
|
---|
16 | // never iterate from multiple threads at the same time
|
---|
17 |
|
---|
18 | namespace Common
|
---|
19 | {
|
---|
20 | /// <summary>
|
---|
21 | /// Simple collection class which can be changed from within iteration.
|
---|
22 | /// </summary>
|
---|
23 | /// <typeparam name="T">type of collected objects</typeparam>
|
---|
24 | class ToughSet<T> : IEnumerable<T>
|
---|
25 | {
|
---|
26 | List<T> list = new List<T>();
|
---|
27 | List<Enumerator<T>> enumerators = new List<Enumerator<T>>(4); // list will rarely have more than 1 element
|
---|
28 |
|
---|
29 | /// <summary>
|
---|
30 | /// Creates an empty collection.
|
---|
31 | /// </summary>
|
---|
32 | public ToughSet() { }
|
---|
33 |
|
---|
34 | /// <summary>
|
---|
35 | /// Number of elements contained in the collection.
|
---|
36 | /// </summary>
|
---|
37 | public int Count { get { return list.Count; } }
|
---|
38 |
|
---|
39 | /// <summary>
|
---|
40 | /// Adds an object to the collection.
|
---|
41 | /// </summary>
|
---|
42 | /// <param name="item">The object to be added.</param>
|
---|
43 | public void Add(T item) { list.Add(item); }
|
---|
44 |
|
---|
45 | /// <summary>
|
---|
46 | /// Removes the current object of an iteration from the collection. Can only be called from within foreach loop.
|
---|
47 | /// </summary>
|
---|
48 | public void RemoveCurrent()
|
---|
49 | {
|
---|
50 | if (enumerators.Count == 0)
|
---|
51 | throw new Exception("ToughSet.Remove() can only be called from within foreach loop!");
|
---|
52 | Enumerator<T> innerEnumerator = enumerators[enumerators.Count - 1];
|
---|
53 | if (!innerEnumerator.currentWasRemoved)
|
---|
54 | {
|
---|
55 | int removeIndex = innerEnumerator.index;
|
---|
56 | if (removeIndex >= 0 && removeIndex < list.Count)
|
---|
57 | {
|
---|
58 | list.RemoveAt(removeIndex);
|
---|
59 | foreach (Enumerator<T> enumerator in enumerators)
|
---|
60 | enumerator.RemoveAt(removeIndex);
|
---|
61 | }
|
---|
62 | }
|
---|
63 | }
|
---|
64 |
|
---|
65 | void EnumerationEnded(Enumerator<T> enumerator) // parameter is only for proof of correctness
|
---|
66 | {
|
---|
67 | if (enumerators.Count == 0)
|
---|
68 | throw new Exception("Error in ToughSet: Only started foreach loop can end!");
|
---|
69 | Enumerator<T> innerEnumerator = enumerators[enumerators.Count - 1];
|
---|
70 | if (enumerator != innerEnumerator)
|
---|
71 | throw new Exception("Error in ToughSet: Only most inner foreach loop can end!");
|
---|
72 | enumerator.DisposeEvent -= EnumerationEnded;
|
---|
73 | enumerators.RemoveAt(enumerators.Count - 1);
|
---|
74 | }
|
---|
75 |
|
---|
76 | #region IEnumerable members
|
---|
77 | class Enumerator<TEn> : IEnumerator<TEn>
|
---|
78 | {
|
---|
79 | ToughSet<TEn> parent;
|
---|
80 | public int index;
|
---|
81 | public bool currentWasRemoved = false;
|
---|
82 | TEn current;
|
---|
83 |
|
---|
84 | public Enumerator(ToughSet<TEn> parent)
|
---|
85 | {
|
---|
86 | this.parent = parent;
|
---|
87 | index = -1;
|
---|
88 | }
|
---|
89 |
|
---|
90 | public void RemoveAt(int removeIndex)
|
---|
91 | {
|
---|
92 | if (removeIndex == index)
|
---|
93 | currentWasRemoved = true;
|
---|
94 | if (removeIndex <= index)
|
---|
95 | index--;
|
---|
96 | }
|
---|
97 |
|
---|
98 | public delegate void DisposeEventHandler(Enumerator<TEn> enumerator);
|
---|
99 | public event DisposeEventHandler DisposeEvent;
|
---|
100 |
|
---|
101 | #region IEnumerator Members
|
---|
102 | public void Reset() { index = -1; }
|
---|
103 | public TEn Current { get { return current; } }
|
---|
104 | object System.Collections.IEnumerator.Current { get { return current; } }
|
---|
105 | public void Dispose() { DisposeEvent(this); }
|
---|
106 |
|
---|
107 | public bool MoveNext()
|
---|
108 | {
|
---|
109 | index++;
|
---|
110 | if (index < parent.list.Count)
|
---|
111 | {
|
---|
112 | current = parent.list[index];
|
---|
113 | currentWasRemoved = false;
|
---|
114 | return true;
|
---|
115 | }
|
---|
116 | else
|
---|
117 | return false;
|
---|
118 | }
|
---|
119 | #endregion
|
---|
120 | }
|
---|
121 |
|
---|
122 | public IEnumerator<T> GetEnumerator()
|
---|
123 | {
|
---|
124 | Enumerator<T> enumerator = new Enumerator<T>(this);
|
---|
125 | enumerators.Add(enumerator);
|
---|
126 | enumerator.DisposeEvent += EnumerationEnded;
|
---|
127 | return enumerator;
|
---|
128 | }
|
---|
129 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
|
---|
130 | #endregion
|
---|
131 | }
|
---|
132 | }
|
---|
133 |
|
---|
134 | /*
|
---|
135 | class was tested with the following code:
|
---|
136 |
|
---|
137 | ToughSet<int> ts = new ToughSet<int>();
|
---|
138 | for (int i = 0; i < 20; i++)
|
---|
139 | ts.Add(i);
|
---|
140 |
|
---|
141 | foreach (int i in ts)
|
---|
142 | { // should iterate 0-1-2-3-5-6-7-8-9-11-13-14-15-16-17
|
---|
143 | if (i == 3)
|
---|
144 | {
|
---|
145 | foreach (int j in ts)
|
---|
146 | {
|
---|
147 | if (j == 1 || j == 4 || j ==12)
|
---|
148 | ts.Remove();
|
---|
149 | if (j == 14)
|
---|
150 | break;
|
---|
151 | }
|
---|
152 | }
|
---|
153 | if (i == 9)
|
---|
154 | {
|
---|
155 | foreach (int j in ts)
|
---|
156 | {
|
---|
157 | if (j == 0 || j == 9 || j == 10)
|
---|
158 | ts.Remove();
|
---|
159 | if (j == 16)
|
---|
160 | break;
|
---|
161 | }
|
---|
162 | }
|
---|
163 | if (i == 8 || i == 9 || i == 16)
|
---|
164 | ts.Remove();
|
---|
165 | if (i == 17)
|
---|
166 | break;
|
---|
167 | }
|
---|
168 | foreach (int i in ts)
|
---|
169 | { // should iterate 2-3-5-6-7-11-13-14-15-17-18-19
|
---|
170 | }
|
---|
171 | ts.Remove(); // should throw exception
|
---|
172 | */
|
---|