Click here to Skip to main content
Click here to Skip to main content

Yet Another C# set class

, 16 Oct 2004
Rate this:
Please Sign up or sign in to vote.
A C# set class that utilizes enum types as sets of flags.

Introduction

This article presents yet another C# set class, namely CSet. CSet utilizes enum types as sets of flags. CSet provides static methods only. The parameters of those methods are of type Object. As long as those parameters represent integer values or any other type that can function as an integer value, everything works fine. Enum constants are numeric constants so there should be no problem.

The actual operations are done by converting/typecasting the passed in object parameters to integers and then applying bit-wise operations.

I'm coming from Borland Delphi to C# and I'm just a C# and .NET novice. Delphi provides a set type and I miss that in C#. So I decided to code one.

Well, there is already a C# set class in CodeProject, described in the article "A C# sets class" by Richard Bothne and Jim Showalter, but that Set class works like a list where you construct a Set object and then add objects, etc. I needed something more similar to Delphi's set types, so here it is.

Sets explained

A set type is a data type which defines a collection of values of the same base type. C# does not provide a set type but we can simulate set variables.

enum Day { Saturday, Sunday, Monday, Tuesday, Wednesday, 
                                     Thursday, Friday }; // a "base type"
....
Day WeekEnd = Day.Saturday| Day.Sunday; // a "set"
Day WorkDays = Day.Monday | Day.Tuesday | Day.Wednesday 
                           | Day.Thursday | Day.Friday ; // a "set"

It is not meaningful for a value to be included twice in a set, although it is not an error if you do that.

There are some operations we can apply to sets:

  • Union is the construction of a new set which contains the elements of two other sets.
  • Intersection is the construction of a new set which contains just the common elements of two other sets.
  • Difference is the construction of a new set which contains the not common elements of two other sets.
  • Membership is a check operation that results in true if a set A is a subset of a set B.

Sets are useful in solving many programming problems, and they promote code clarity and readability.

Not all languages provide a set type. In those cases, one could use carefully assigned integer constants and then apply bitwise operations on them. Bit-wise operations are not an un-common method in C/C++ and Win32 programming.

const int cLeft = 1; 
const int cUp = 2; 
const int cRight = 4; 
const int cDown = 8;
...
int Directions = cRight | cDown; // Union

if ((cRight & Directions) == cRight)
// Membership - is cRight included in Directions?
    MessageBox.Show("Going right");

.NET FlagsAttribute attribute and C#'s enum constant assignment can be used in order to simulate set operations in C# code.

FlagsAttribute class and sets

At the FlagsAttribute class preview topic, .NET documentation states that "...(the FlagsAttribute class) Indicates that an enumeration can be treated as a bit field; that is, a set of flags..." and that "...Bit fields can be combined using a bitwise OR operation, whereas enumerated constants cannot...".

Based on the above, the following should not be legal:

enum Day { Saturday, Sunday, Monday, Tuesday, Wednesday, Thursday, Friday };
....
Day A = Day.Friday | Day.Tuesday;

Well, legal or not, it compiles fine.

More one that, one reading the documentation regarding the FlagsAttribute class, may think that all he needs in order to turn his enum type into a bit-field (call me set) is just to add that FlagsAttribute attribute above his type declaration. Well, it takes more than that. You need to carefully assign literal integer values to each of the enum constants. The first constant = 1, the second = 2, the third = 4, and so on. That way, your enum constants simulate a bit-mask.

I believe enums defined that way should not exceed 32 elements. Not tested though.

Anyway, if you assign those values to your constants the way I described, then you can use OR-ing, AND-ing and XOR-ing against them. And it seems, we get corrects results even if we omit the FlagsAttribute attribute definition.

// if you un-comment the attribute the only difference
// is that the first message box displays
// the string value, that is the day names of the set,
// else you get its numeric value 
//[FlagsAttribute] 
enum Day { Saturday = 1, Sunday = 2, Monday = 4, Tuesday = 8, 
                  Wednesday = 16, Thursday = 32, Friday = 64 };
...
Day A = Day.Saturday | Day.Friday | Day.Sunday; 
Day B = Day.Sunday; 
MessageBox.Show(A.ToString()); 

bool b = (((int)B & (int)A) == (int)B); // ugly eh? isn't it? 
MessageBox.Show(b.ToString());

Of course, to get the most, just apply the FlagsAttribute attribute to your enum.

Simulating sets

Now the rest is easy. In order to simulate sets, we just convert those enum constants to integers and then we apply bit-masking to them. Here are the those operations.

// Union
(int)A | (int)B

// Intersection
(int)A & (int)B

// Difference
(int)((int)A | (int)B) ^ (int)B

// Membership
(((int)A & (int)B) == (int)A)

We can code a function for each of the above operations. A and B should be defined of type Object. Of course, we need to take some precautions and test the validity of any argument passed.

if ((A == null) || (B == null)) 
    throw(new ArgumentNullException()); 

if (A.GetType() != B.GetType()) 
    throw(new Exception("Different set types"));

The CSet class

So, now we can have the full class coded using the above. Our CSet class is going to have just static methods.

public class CSet: object 
{
    // Union
    public static object Or(object A, object B)
    {
        if ((A == null) || (B == null)) throw(new ArgumentNullException());
        if (A.GetType() != B.GetType()) throw(new Exception("Different set types"));
        return (int)A | (int)B;
    }
    public static object Union(object A, object B)
    {
        if ((A == null) || (B == null)) throw(new ArgumentNullException());
        if (A.GetType() != B.GetType()) throw(new Exception("Different set types"));
        return (int)A | (int)B;
    }

    // Intersection
    public static object And(object A, object B)
    {
        if ((A == null) || (B == null)) throw(new ArgumentNullException());
        if (A.GetType() != B.GetType()) throw(new Exception("Different set types"));
        return (int)A & (int)B;
    }
    public static object Intersection(object A, object B)
    {
        if ((A == null) || (B == null)) throw(new ArgumentNullException());
        if (A.GetType() != B.GetType()) throw(new Exception("Different set types"));
        return (int)A & (int)B;
    }

    // Difference 
    public static object Xor(object A, object B)
    {
        if ((A == null) || (B == null)) throw(new ArgumentNullException());
        if (A.GetType() != B.GetType()) throw(new Exception("Different set types"));
        return (int)((int)A | (int)B) ^ (int)B;
    }
    public static object Diff(object A, object B)
    {
        if ((A == null) || (B == null)) throw(new ArgumentNullException());
        if (A.GetType() != B.GetType()) throw(new Exception("Different set types"));
        return (int)((int)A | (int)B) ^ (int)B;
    }

    // Membership - A in B, A can be a single value or a set. 
    public static bool In(object A, object B)
    {
        if ((A == null) || (B == null)) throw(new ArgumentNullException());
        if (A.GetType() != B.GetType()) throw(new Exception("Different set types"));
        return (((int)A & (int)B) == (int)A);
    }
}

Using the code

To use the code, copy the class into a source document, choosing an appropriate namespace and then...

//[FlagsAttribute] 
//enum Day { Saturday, Sunday, Monday, Tuesday, Wednesday, Thursday, Friday }; 
[FlagsAttribute] 
enum Day { Saturday = 1, Sunday = 2, Monday = 4, Tuesday = 8, 
                  Wednesday = 16, Thursday = 32, Friday = 64 }; 

private void button1_Click(object sender, System.EventArgs e) 
{
    // Union
    Day A = Day.Wednesday;
    Day B = Day.Friday | Day.Tuesday; // OR using |
    Day C = (Day)CSet.Or(A, B); // OR using CSet.Or()
    MessageBox.Show(C.ToString());

    // Intersection
    C = (Day)CSet.And(Day.Tuesday, B);
    MessageBox.Show(C.ToString());

    // Difference
    A = Day.Wednesday;
    B = Day.Wednesday | Day.Wednesday;
    C = (Day)CSet.Xor(A, B);
    MessageBox.Show(C.ToString());

    // Membership
    A = Day.Wednesday | Day.Tuesday;
    B = Day.Wednesday;
    MessageBox.Show(CSet.In(B, A).ToString());
}

That's it. I hope you'll find this tiny class useful.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Theo Bebekis
Software Developer
Greece Greece
I'm a C# and Delphi developer and trainer from Thessaloniki, Greece.

Comments and Discussions

 
GeneralJust what I was looking for Pinmembermrsnipey24-Jul-07 21:21 
GeneralThank you! PinmemberChristopher Scholten27-Jul-05 19:04 
GeneralBase class PinmemberIgor Vigdorchik16-Oct-04 12:00 
GeneralRe: Base class Pinmembernorm.net17-Oct-04 21:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140721.1 | Last Updated 16 Oct 2004
Article Copyright 2004 by Theo Bebekis
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid