views:

219

answers:

2

I need a collection that behaves as Set and preserves order of element insertion.

Is there one or I'll have to implement it myself?

What would the best implementation be?

+2  A: 

It doesn't exist in .NET, but you can emulate it using a List and the Distinct LINQ extension method, which should preserve the order of the underlying List.

David Pfeffer
It is extremely frustrating isn't it? Same frustration in PowerShell which uses Hashtable for dictionaries which also loses insertion order.
Josh Einstein
I need fast indexing but can afford slow insertions so I'll resort to `List` and will write extension method `AddUnique` to achieve Set semantics.
Konstantin Spirin
+1  A: 

Will an OrderedDictionary do what you want?

Although it is not generic (meaning everything it returns has to be cast or unboxed) and is a dictionary (not a set), it will behave the way you want. You could easily just use some arbitrary value like null or true as values and let the keys be the members of your set.

Here's a possible implementation:

public class OrderedSet : ICollection, IEnumerable
{
    OrderedDictionary dict = new OrderedDictionary();
    public void Add(object member)
    {
        dict.Add(member, null);
    }
    public void Clear()
    {
        dict.Clear();
    }
    public void CopyTo(Array array, int index)
    {
        for (int i = 0; i < Count; i++)
            array[i + index] = dict[i];
    }
    public bool Contains(object member)
    {
        return dict.Contains(member);
    }
    public void Insert(int index, object member)
    {
        dict.Insert(index, member, null);
    }
    public void Remove(object member)
    {
        dict.Remove(member);
    }
    public void RemoveAt(int index)
    {
        dict.RemoveAt(index);
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return dict.Keys.GetEnumerator();
    }

    public int Count { get { return dict.Count; } }
    public ICollection Members { get { return dict.Keys; } }
    bool ICollection.IsSynchronized { get { return dict.IsSynchronized; } }
    object ICollection.SyncRoot { get { return dict.SyncRoot; } }
}
Gabe
It will work, but he would have to basically use only the keys and not the values. Not ideal, but it'll get the job done.
David Pfeffer