Visual C# 2005 Guide
The Set Class

Very early on this guide, we touched upon the fact that we could use the ArrayList class to simulate some basic set operations. If we want to be able to use standard set operations, we need to do a bit more work.

Set Basics

Sets will be displayed on screen in square brackets using commas to separate values. [3, hello, orange, true] is an example of the output we would want. [] indicates an empty set. There are four main operations that we are going to simulate with the Set class that we will make. These are,

  1. Union - The union of Set1 and Set2 would include each value from Set1 and each value from Set2 but no duplicates.
  2. Difference - The difference operation applied to Set1 and Set2 would result in a list of all the items in Set1 that do not appear in Set2.
  3. Intersection - The intersection of Set1 and Set2 results in a set which includes all of the values that exist in both sets.
  4. Membership - Checking to see if a value is in a set.

Designing The Class - Fields

One of the basic principles of Object Oriented Programming is that of encapsulation. That means that our Set class should contain all of the data and methods that are required for operating with the set. We will create a separate code file for doing this. Go to the Project menu and choose Add Class. Call your class CSet.cs.

Start by altering the using statements so that we can use the ArrayList class.

Our class needs only one data field, the arraylist that holds the set. We add this first to our class,

private ArrayList ListData;

We use the private keyword to indicate that the data stored in this variable can only be accessed from code within the class. We could make this a public field, but that is not good practice and not remotely necessary for our purposes.

Designing The Class - Constructors

Next we need a constructor. The constructor is a method that is executed when the class is instantiated (an instance of the class is declared and initialised). We will use 3 constructors. They will all have the same name but different parameters. This is called overloading and is a common feature of many of the built-in classes in the .NET framework.

public CSet()
{
   ListData = new ArrayList();
}

public CSet(ArrayList newSetData)
{
   ListData = new ArrayList();
   ListData.AddRange(newSetData);
}

public CSet(object[] items)
{
   ListData = new ArrayList();
   ListData.AddRange(items);
}

Notice here that our methods are public. This is because they will be called from outside of this class. The three different methods are used so that the class can be instantiated as an empty set, by supplying an arraylist of values or an array. The use of the data type object is to cater for the fact that the arraylist can store multiple data types.

Designing The Class - Basic Methods

We will need a whole host of methods to make our class work as it should. The first group of methods to add are those for providing basic information about the class.

public void Add(object item)
{
   if (!ListData.Contains(item))
   {
      ListData.Add(item);
   }
}

public void AddRange(object[] item)
{
   foreach (object i in item)
   {
      if (!ListData.Contains(i))
      {
         ListData.Add(i);
      }
   }
}

public void Remove(object item)
{
   if (ListData.Contains(item))
   {
      ListData.Remove(item);
   }
}

public int Size()
{
   return ListData.Count;
}

public string SetAsString()
{
   if (ListData.Count == 0)
   {
      return "[]";
   }
   else
   {
      string setOutput = "[";
      setOutput += ListData[0];
      for (int i = 1; i < ListData.Count; i++)
      {
         setOutput = setOutput + ", " + ListData[i];
      }
      setOutput += "]";
      return setOutput;
   }
}

Most of these methods are obvious. Notice that we only want to add items to the list if they aren't already in the list. If they are, we simply don't bother.

Designing The Class - Set Operation Methods

Now to program those set operation methods that we had at the top of the page.

The easiest method is the one to check if an item is in the set,

public bool IsInSet(object item)
{
   return ListData.Contains(item);
}

The other methods will require us to examine the values in other sets. We can't do this if we don't have access to the arraylist that stores the data. We can create a property for our class to do this. All we want to do is give the programmer a copy of the values in the arraylist as an arraylist. We do this as follows,

public ArrayList Values
{
   get { return ListData; }
}

The get statement indicates the kind of access that is being attempted through the property. 'Getting' means retrieving the value - not modifying the list. We could also include a set statement with programming logic for setting the values. Properties look like fields to the programmer in that you do not put brackets at the end of them. However, strictly speaking, properties are special accessor methods. Collectively these are often referred to as getters and setters. This doesn't mean that there is no point making the data member private - it allows the class designer to control how values are accessed in a private data member.

Now for the remaining set operations,

public CSet Union(CSet setToAdd)
{
   ArrayList addList = setToAdd.Values;
   CSet newSet = new CSet(addList);
   foreach (object o in ListData)
   {
      newSet.Add(o);
   }
   return newSet;
}

public CSet Difference(CSet setToSubtract)
{
   ArrayList subList = setToSubtract.Values;
   CSet newSet = new CSet(ListData);
   foreach (object o in subList)
   {
      newSet.Remove(o);
   }
   return newSet;
}

public CSet Intersection(CSet setToIntersect)
{
   CSet unionSet = Union(setToIntersect);
   CSet newSet = new CSet();
   ArrayList addList = unionSet.Values;
   foreach (object o in addList)
   {
      if (ListData.Contains(o) && setToIntersect.IsInSet(o))
      {
         newSet.Add(o);
      }
   }
   return newSet;
}

Notice that we are returning sets when we do this. You can get a listing of the entire class by clicking Cset Class.

Using The Class

Once you have created the class as a separate .cs file, you can use it any program that it has been added to. Anytime you write a program that works with sets, you can import the set class and use it. The following example shows how the class can be used. This would be done within the Program.cs code window.

static void Main(string[] args)
{
   CSet setA = new CSet();
   setA.Add(3);
   setA.Add(9);
   setA.Add(13);
   setA.Add(18);
   Console.WriteLine("Set A: {0}", setA.SetAsString());
   CSet setB = new CSet();
   setB.Add(5);
   setB.Add(9);
   setB.Add(17);
   setB.Add(18);
   Console.WriteLine("Set B: {0}", setB.SetAsString());
   CSet unionSet = setA.Union(setB);
   Console.WriteLine("Union: Set A + Set B = {0}", unionSet.SetAsString());
   CSet differenceSet = setA.Difference(setB);
   Console.WriteLine("Difference: Set A - Set B = {0}", differenceSet.SetAsString());
   differenceSet = setB.Difference(setA);
   Console.WriteLine("Difference: Set B - Set A = {0}", differenceSet.SetAsString());
   CSet intersectSet = setA.Intersection(setB);
   Console.WriteLine("Intersection: Set A * Set B = {0}", intersectSet.SetAsString());
   Console.ReadLine();
}

Having A Go (Sets)

  1. Set B is a subset of Set A if Set A is larger than Set B and all the items in Set B are in Set A. Add a method of the class to check if a set is a subset of the set.
  2. Write a program using the set class to check if the following statements are true,
    1. A Intersect B = B Intersect A
    2. A Intersect (B Intersect C) = (A Intersect B) Intersect C
    3. A Union (B Union C) = (A Union B) Union C
    4. A Intersect (B Union C) = (A Intersect B) Union C
    5. A Intersect (A Union B) = A
    6. A Difference (B Union C) = (A Difference B) Intersect (A Difference C)
    7. A Difference (B Intersect C) = (A Difference B) Union (A Difference C)