15

What is a

  • preferably generic;
  • unique(IComparable/IEquitable) valued

collection of objects for .NET 2?

(à la List<T>, or an equivalent of HashSet<T> from .NET 3.5, but without ordered items)

serhio
  • 28,010
  • 62
  • 221
  • 374
  • 1
    Please see http://stackoverflow.com/questions/687034/using-hashset-in-c-2-0-compatible-with-3-5. – Andrew Hare Feb 04 '10 at 14:26
  • 1
    Why do you put "à la `List`" in your question? It's not a unique items list. – herzmeister Feb 04 '10 at 14:34
  • 1
    If you're an evil h4x0r you can just roll out the single System.Core.dll assembly with your application. No need to install the complete .NET 3.5 framework. – herzmeister Feb 04 '10 at 14:36
  • 2
    @serhio - You only need to install .NET 3.5 to get a copy of `System.Core.dll`, once you have a copy you can simply add a reference to the file and bundle it with your application. – Andrew Hare Feb 04 '10 at 14:38
  • HasSet now has no `.AsReadOnly()` method... :) – serhio Feb 04 '10 at 15:19
  • @Andrew Hare: Are you sure that deployment scenario works with the license for the .Net Framework? Microsoft often does not allow the partial deployment of their APIs like that. – Chris Pitman Feb 04 '10 at 15:24
  • @Chris - It depends on how you plan on deploying it. For instance, if this is a web application then I don't see a problem. – Andrew Hare Feb 04 '10 at 15:28
  • @Andrew: and if this is a desktop application? – serhio Feb 04 '10 at 15:35
  • @herzmeister der welten: I put "à la", bacause I want a unique collection à la List<>. This does not mean that the List<> is unique. – serhio Jul 13 '10 at 11:12

4 Answers4

9

Unfortunately, the first good framework class for this is the HashSet, which you will only get access to with .Net 3.5.

If you are stuck in previous versions., the options are not as nice. The most common is to usea Dictionary type where the Key is the value that you are trying to store. You can wrap this in your own class fairly easily.

If you are willing to go outside the framework all together, there are collections of data structures for .Net, such as NGenerics.

Chris Pitman
  • 12,990
  • 3
  • 41
  • 56
  • Thanks for this. I used System.Collections.Generic.Dictionary to get exactly the effect I needed. I wrapped it in a try/catch(ArgumentException) – AJ. Sep 30 '10 at 09:14
  • Is there any Unique Queue in .Net 4.0 e.g. if a queue contains 1 at any level and trying to add 1 again should throw error. – Mubashar May 11 '12 at 18:02
  • 1
    @MubasharAhmad No, that isn't a common requirement and likely will never be added into the framework. You could always create this behavior by having both a Queue and a HashSet, and then update both on any operation. – Chris Pitman May 11 '12 at 20:08
3

What you need is a Set, as far as I remember there was no Set implementation in 2.0. You can check this out.

Edit: If you really want to implement your own, something like this would do the job in expense of performance on inserts: (I did not test the functionality)

class UniqueList<T> : IList<T>
{
    private IList<T> m_InternalList;

    public UniqueList(IList<T> list)
    {
        m_InternalList = list;
    }

    public System.Collections.ObjectModel.ReadOnlyCollection<T> AsReadOnly()
    {
        return new System.Collections.ObjectModel.ReadOnlyCollection<T>(this);
    }

    #region IList<T> Members

    public int IndexOf(T item)
    {
        return m_InternalList.IndexOf(item);
    }

    public void Insert(int index, T item)
    {
        if (!m_InternalList.Contains(item))
            m_InternalList.Insert(index, item);
    }

    public void RemoveAt(int index)
    {
        m_InternalList.RemoveAt(index);
    }

    public T this[int index]
    {
        get
        {
            return m_InternalList[index];
        }
        set
        {
            if (!m_InternalList.Contains(value))
                m_InternalList[index] = value;
        }
    }

    #endregion

    #region ICollection<T> Members

    public void Add(T item)
    {
        if (!m_InternalList.Contains(item))
            m_InternalList.Add(item);
    }

    public void Clear()
    {
        m_InternalList.Clear();
    }

    public bool Contains(T item)
    {
        return m_InternalList.Contains(item);
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        m_InternalList.CopyTo(array, arrayIndex);
    }

    public int Count
    {
        get { return m_InternalList.Count; }
    }

    public bool IsReadOnly
    {
        get { return m_InternalList.IsReadOnly; }
    }

    public bool Remove(T item)
    {
        return m_InternalList.Remove(item);
    }

    #endregion

    #region IEnumerable<T> Members

    public IEnumerator<T> GetEnumerator()
    {
        return m_InternalList.GetEnumerator();
    }

    #endregion

    #region IEnumerable Members

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return m_InternalList.GetEnumerator();
    }

    #endregion
}
Ekin Koc
  • 2,996
  • 20
  • 24
  • it's a little "hard" solution for me importing a "external" project just for a unique collection. I'd prefer or write a custom collection by myself or a better solution just use a "trick" on the existing framework classes. – serhio Feb 04 '10 at 14:25
  • 4
    @serhio: I think writing your own collection class is 'harder' then just referencing a 3rd party component that has been proved to work. I think you suffer from the 'not invented here' syndrome. :) – Frederik Gheysels Feb 04 '10 at 14:29
  • one method that I really need is `.AsReadOnly()` – serhio Feb 04 '10 at 15:23
2

You can use the HashedSet<T> collection that is defined in the Iesi.Collections assembly. This is an open source project, which is also used by NHibernate.

Frederik Gheysels
  • 56,135
  • 11
  • 101
  • 154
1

We used to use PowerCollections Set class for that in .NET 2. It worked quite well. There was a lot of nice stuff in the library.

Mike Two
  • 44,935
  • 9
  • 80
  • 96