Steve Barnett wrote:
I have a collection which I need to navigate through in several different
ways. Is there any way to structure my class such that I can have several
different iterators?
Yes, give it properties returning different views:
class MyClass: ICollection<Foo> {
ICollection<Foo> AllNodes { get { return this; } }
ICollection<Foo> BlueNodes { get { return /* something else */; } }
}
Which would allow you to rewrite to:
Ideally, I'd like to do something like:
// Iterate whole collection
foreach (object in collection)
foreach( object o in myClassIntances )
or
foreach( object o in myClassIntances.AllNodes )
// Iterate over all objects with the "open" flag set
foreach (object in collection.opennodes)
foreach ( object o in myClassInstance.OpenNodes )
// Iterate over all blue objects
foreach (object in collection.bluenodes)
My general suggestion is to write a "filter-dictionary", so you can simply:
Dictionary<Foo,Bar> dict = ...;
foreach ( Foo foo in
new FilterCollection(
dict.Keys,
delegate(Foo x) { return x.isBlue(); })
...;
If you often do the same transformation, give it a name:
public static ICollection<Foo> BlueNodes(ICollection<Foo> nodes) {
return new FilterCollection(
node,
delegate(Foo x) { return x.isBlue(); });
}
and if your "masert-dictionary" collection should supply callers with
different views, as suggested at the top of this post:
public class MyDict: IDictionary<Foo, Bar> {
...
ICollection Foo BlueKeys { get { return BlueNodes(Keys); } }
}
Here as an implementation suggestion:
/// <summary>
/// Filters an ICollection to only contains the values satisfying a
predicate
/// </summary>
public class FilterCollection<T> : ICollection<T>
{
public ICollection<T> Parent;
public delegate bool FilterFunction(T t);
public FilterFunction Filter;
public FilterCollection(ICollection<T> parent, FilterFunction
filter)
{
this.Parent = parent;
this.Filter = filter;
}
public int Count
{
get
{
int count = 0;
foreach (T t in Parent)
if (Filter(t))
++count;
return count;
}
}
public bool IsReadOnly { get { return Parent.IsReadOnly; } }
public void Add(T item)
{
if (!Filter(item))
throw new ArgumentException(string.Format("Filter
rejected {0}", item));
else
Parent.Add(item);
}
public void Clear() { throw new Immutable(this); }
public bool Contains(T item) { return Parent.Contains(item) &&
Filter(item); }
public void CopyTo(T[] array, int arrayIndex)
{
foreach (T t in this)
array[arrayIndex++] = t;
}
public bool Remove(T item) { return Filter(item) &&
Parent.Remove(item); }
public IEnumerator<T> GetEnumerator()
{
foreach (T t in Parent)
if (Filter(t))
yield return t;
}
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
/// <summary>
/// Filters an IDictionary to only contain the entries satisfying a
predicate
/// </summary>
public class FilterDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
public IDictionary<TKey, TValue> Parent;
public delegate bool FilterFunction(TKey key, TValue value);
public FilterFunction Filter;
public FilterDictionary(IDictionary<TKey, TValue> parent,
FilterFunction filter)
{
this.Parent = parent;
this.Filter = filter;
}
public virtual TValue this[TKey key]
{
get
{
TValue val = Parent[key];
if (Filter(key, val))
return val;
else
throw new KeyNotFoundException(string.Format("Not
accepted by filter: {0}->{1}", key, val));
}
set
{
if (Filter(key, value))
Parent[key] = value;
else
throw new KeyNotFoundException(string.Format("Not
accepted by filter: {0}", key));
}
}
protected bool keyFilter(TKey key) { return Filter(key,
Parent[key]); }
public ICollection<TKey> Keys { get { return new
FilterCollection<TKey>(Parent.Keys, keyFilter); } }
public ICollection<TValue> Values { get { throw new
NotImplementedException("Pending need for implementation"); } }
public int Count { get { return Keys.Count; } }
public bool IsReadOnly { get { return Parent.IsReadOnly; } }
public bool ContainsKey(TKey key)
{
TValue val;
return TryGetValue(key, out val);
}
public void Add(TKey key, TValue value)
{
if (!Filter(key, value))
throw new ArgumentException(string.Format("Filter
rejected {0}=>{1}", key, value));
Parent.Add(key, value);
}
public bool Remove(TKey key)
{
TValue val;
return TryGetValue(key, out val) && Parent.Remove(key);
}
public bool TryGetValue(TKey key, out TValue value) { return
Parent.TryGetValue(key, out value) && Filter(key, value); }
public void Add(TKey item) { throw new
NotSupportedException("Cannot add key-only"); }
public void Clear() { throw new
NotSupportedException(string.Format("Cannot clear {0}", GetType())); }
public bool Contains(TKey item) { return ContainsKey(item); }
public void CopyTo(TKey[] array, int arrayIndex) {
this.Keys.CopyTo(array, arrayIndex); }
public IEnumerator<TKey> GetEnumerator()
{ return Keys.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { throw new
NotImplementedException(); }
public DictionaryKeyValuePairCollection<TKey, TValue>
AsCollection(IEqualityComparer<TValue> comparer)
{ return new DictionaryKeyValuePairCollection<TKey,
TValue>(this, comparer); }
public DictionaryKeyValuePairCollection<TKey, TValue> AsCollection()
{ return new DictionaryKeyValuePairCollection<TKey, TValue>(this); }
IEnumerator<KeyValuePair<TKey, TValue>>
IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{ return AsCollection().GetEnumerator(); }
public bool Remove(KeyValuePair<TKey, TValue> kvp)
{ return AsCollection().Remove(kvp); }
public bool Contains(KeyValuePair<TKey, TValue> kvp)
{ return AsCollection().Contains(kvp); }
public void Add(KeyValuePair<TKey, TValue> kvp)
{ AsCollection().Add(kvp); }
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int index)
{ AsCollection().CopyTo(array, index); }
}
}
--
Helge Jensen
mailto:he**********@slog.dk
sip:he**********@slog.dk
-=> Sebastian cover-music:
http://ungdomshus.nu <=-