Set Operations with C# Generics

C# Generics are powerful, but there are not many generic classes beyond List and Dictionary. Especially for Set Operations Generics are missing.

That’s whay I created a small class for Set Operations called Set that contains the basic Set Operations, like Union, Intersect and Difference.

Here’s a small example how to use it:

public enum DataSource
{
    Database, XML, File, Stream, Http, Ftp
}

static void Main(string[] args)
{
    Set<DataSource> firstSet = new Set<DataSource>(
    DataSource.Database, DataSource.File, DataSource.XML);
    
    Set<DataSource> secondSet = new Set<DataSource>(
    DataSource.Http, DataSource.Ftp, DataSource.Stream, DataSource.XML, DataSource.File);
    
    Set<DataSource> union = firstSet.Union(secondSet);
    // result = Database, File, Xml, Http, Ftp, Stream
    
    Set<DataSource> intersect = firstSet.Intersect(secondSet);
    // result = Xml, File
    
    Set<DataSource> difference = firstSet.Difference(secondSet);
    // result = Http, Ftp, Stream
}

And below you’ll find the complete implementation.

Cheers
- Gerhard

kick it on DotNetKicks.com



using System.Collections;
using System.Collections.Generic;

namespace SetExample
{
    /// <summary>
    /// Set class
    /// </summary>
    /// <typeparam name="TElement"></typeparam>
    public class Set<TElement> : ICollection<TElement>
    {
        private List<TElement> internalList = new List<TElement>();
        
        /// <summary>
        /// Initializes a new instance of the <see cref="Set&lt;TElement&gt;"/> class.
        /// </summary>
        public Set()
        {
            
        }
        
        /// <summary>
        /// Initializes a new instance of the <see cref="Set&lt;TElement&gt;"/> class.
        /// </summary>
        /// <param name="elements">The elements.</param>
        public Set(params TElement[] elements)
        {
            AddRange(elements);
        }
        
        /// <summary>
        /// Adds the range.
        /// </summary>
        /// <param name="range">The range.</param>
        public void AddRange(IEnumerable<TElement> range)
        {
            foreach (TElement element in range)
            Add(element);
        }
        
        /// <summary>
        /// Unions the specified set.
        /// </summary>
        /// <param name="set">The set.</param>
        /// <returns></returns>
        public Set<TElement> Union(Set<TElement> set)
        {
            Set<TElement> result = new Set<TElement>();
            
            result.AddRange(this);
            result.AddRange(set);
            
            return result;
        }
        
        /// <summary>
        /// Intersects the specified set.
        /// </summary>
        /// <param name="set">The set.</param>
        public Set<TElement> Intersect(Set<TElement> set)
        {
            Set<TElement> result = new Set<TElement>();
            
            foreach (TElement element in set)
            if (Contains(element))
            result.Add(element);
            
            return result;
        }
        
        /// <summary>
        /// Differences the specified set.
        /// </summary>
        /// <param name="set">The set.</param>
        /// <returns></returns>
        public Set<TElement> Difference(Set<TElement> set)
        {
            Set<TElement> result = new Set<TElement>();
            
            foreach (TElement element in set)
            if (!Contains(element))
            result.Add(element);
            
            return result;
        }
        
        
        /// <summary>
        /// Gets or sets the <see cref="TElement"/> at the specified index.
        /// </summary>
        /// <value></value>
        public TElement this[int index]
        {
            get { return internalList[index]; }
            set { internalList[index] = value; }
        }
        
        #region ICollection<TElement> Members
        
        public void Add(TElement item)
        {
            if (!Contains(item))
            internalList.Add(item);
        }
        
        public void Clear()
        {
            internalList.Clear();
        }
        
        public bool Contains(TElement item)
        {
            return internalList.Contains(item);
        }
        
        public void CopyTo(TElement[] array, int arrayIndex)
        {
            internalList.CopyTo(array, arrayIndex);
        }
        
        public bool Remove(TElement item)
        {
            return internalList.Remove(item);
        }
        
        public int Count
        {
            get { return internalList.Count; }
        }
        
        public bool IsReadOnly
        {
            get { return false; }
        }
        
        #endregion
        
        #region IEnumerable<TElement> Members
        
        IEnumerator<TElement> IEnumerable<TElement>.GetEnumerator()
        {
            return internalList.GetEnumerator();
        }
        
        #endregion
        
        #region IEnumerable Members
        
        public IEnumerator GetEnumerator()
        {
            return internalList.GetEnumerator();
        }
        
        #endregion
    }
}

6 Responses to “Set Operations with C# Generics”

  1. Niki Says:

    Did you try out NGenerics? They contain lots of the features that are missing from System.Collections.Generics

  2. B3ardman Says:

    NHibernate comes with an assembly call Iesi.Collections, which contains a number of different implementations of ISet. If you just want a set, then surely that’d do it?

  3. Judah Says:

    The free PowerCollections library also has a set.

    .NET 3.5 has a generic HashSet collection.

  4. Weekly Link Post 12 « Rhonda Tipton’s WebLog Says:

    [...] is a good article on Set Operations with C# Generics by Gerhard [...]

  5. Rahul Says:

    Short, clear and straight implementation. Its nice filling .NET gaps.

  6. Hugo Says:

    Thanks,

    I was missing this!
    Very useful.
    You could add implementation of symmetric difference.
    And the fastest way to compute it, is not the union minus the intersection by the way.


Leave a Reply