Testing

webTiger Logo Wide

.NET Tutorials – 4 – Object Orientated Programming

Microsoft .NET Logo

This tutorial aims to introduce you to the concepts of object-orientated programming in .NET, and the benefits it offers over traditional procedural programming.

In this article:

Preamble

In the previous tutorial, we introduced some of C#’s built-in types such as int, long, decimal, bool, etc. but the heart and soul of the C# language is the ability to create new, complex, programmer defined types that map cleanly onto the characteristics of the problems we are trying to solve. It is this ability that characterises an object orientated programming language.

Classes and Types

The essence of object-orientated programming (OOP) is the creation of new types. A type represents an entity. Sometimes the entity is abstract, such as a data-table or a thread; sometimes it is more tangible such as a button in a window. A type defines the entity’s properties and behaviours.

In C#, and many other object-orientated languages, a type is defined as a class or a structure while individual instances of that type are called objects. Later in this series of tutorials, we will look at other types in C# such as enums, delegates.

The Hello World example we looked at in the previous tutorial declared a single type, the Program class. You are free to rename this class if you like. Remember to rename the name of the source file along with the class, so that the class is easy to find in the project files list using the Solution Explorer. HINT: If you rename the file first, Visual Studio will rename the class for you.

Defining Classes

You declare a class using the class language keyword and then give the class an identifier. Brace characters encapsulate the body of the class. For example:

public class MyStatistics
{
    // TODO: contents of the class go here.
}Code language: C# (cs)

Above is the one of the most basic definitions for a class. Classes can inherit from other classes or inherit from interfaces that they then need to implement. But, we’re getting a bit ahead of ourselves so let’s get back to what is defined immediately above.

Here we have an access modifier, the class keyword, and the name of the class.

Access Modifiers

An access modifier determines the visibility of an entity (class, member, etc.) in the wider code project and beyond. The most common access modifiers are:

  • public. No access restrictions. The entity is visible throughout the code project and in any other code projects its assembly is referenced.
  • internal. The entity is visible only within the current code project. It will be hidden from any projects it is an external reference for.
  • protected. Does not apply to classes, only to members of classes, etc. The entity is visible in its own scope and by any classes derived from its parent.
  • private. The entity is not visible outside its current scope (e.g. for a class, a private member is not visible outside that class itself).
  • protected internal. Similar to protected, but the entity is only visible within the current code project. Any derived class in other code projects will not be able to access it.

Although it is generally desirable to explicitly mark entities with access modifiers, C# will apply default access rules in the absence of them. For example, a class becomes internal by default and a field within a class becomes private by default.

There are also additional rules surrounding access modifiers, such as a class cannot be marked as protected, as this typically applies to members within a class.

Creating Class Instances (aka Objects)

In previous tutorials, we explained the distinction between value types and reference types. C# primitives are value types that are created on the Stack. Objects are reference types, and they are created on the Heap. You can easily differentiate between the two in code because reference types need to be created using the new keyword. For example, if we wanted to create an instance of our MyStatistics class from earlier we could do this:

MyStatistics stats = new MyStatistics();Code language: C# (cs)

The stats variable doesn’t hold the value of the class like it would for a value type. Instead, it holds the address of a newly created instance of MyStatistics in memory on the Heap. The variable is just a pointer to that memory location.

Class Constructors

In the above exmple, we instantiated a new object as an instance of our MyStatistics class using the new keyword. The statement immediately after that keyword is a method call to the class constructor, a special method responsible for initialising the class instance into a valid state.

In our class definition, there was no MyStatistics() method specified, and that’s because the runtime provides a default constructor automatically without you having to explicitly define it.

C# supports parameterised constructors too though, so you would generally only define an explicit constructor if you wanted to pass configuration into the class to control its initialisation. For example, consider the following:

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

namespace LiesDamnLiesAndStatistics
{
    /// <summary>
    /// A very basic representation of a bank account for demo purposes.
    /// </summary>
    public class Account
    {
        #region Private Fields

        /// <summary>
        /// The name of the account holder
        /// </summary>
        private string holderName = "";

        /// <summary>
        /// The account number.
        /// </summary>
        private int accountNumber = 0;

        /// <summary>
        /// The account balance in £'s.
        /// </summary>
        private decimal balance = 0;

        #endregion

        #region Constructor

        /// <summary>
        /// Initialises a new instance of the Account class, and 
        /// configures its initial state.
        /// </summary>
        /// <param name="holderName">The name of the account holder.</param>
        /// <param name="accountNumber">The account number.</param>
        /// <param name="balance">The account balance in £'s.</param>
        public Account(string holderName, int accountNumber, decimal balance)
        {
            this.holderName = holderName;
            this.accountNumber = accountNumber;
            this.balance = balance;
        }

        #endregion

        #region Public Members

        /// <summary>
        /// Displays the account information to the screen
        /// </summary>
        public void DisplayAccountDetails()
        {
            Console.WriteLine("{0} holds Account: {1}, with a balance of {2}",
                this.holderName, this.accountNumber, this.balance.ToString("C"));
        }

        #endregion
    }
}Code language: C# (cs)

Here we’re explicitly defining a parameterised constructor to force consumer instantiating it to provide the minimum data required to configure it into a valid state.

Something to note here is that providing an explicit constructor stops C# from automatically generating a default (parameterless) constructor. If you want to be able to initialise an instance of the class without any parameters you must explicitly declare the parameterless constructor yourself.

Now lets look at how we might use the class.

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

namespace LiesDamnLiesAndStatistics
{
    public class MyStatistics
    {
        private Account[] _myStats;

        public MyStatistics()
        {
            this._myStats = new Account[] 
            {
                new Account("Mr John Smith", 01937849, 45135M),
                new Account("Ms Jane Seymour", 00175932, 2987192M),
                new Account("Mrs Ima Broke", 12940391, 2M)
            };
        }

        public void DisplayMyStats()
        {
            for (int i = 0; i < this._myStats.Length; i++)
            {
                this._myStats[i].DisplayAccountDetails();
            }
        }
    }
}
Code language: C# (cs)

By forcing the Account class constructor to have a minimum amount of information supplied, we ensure we have enough data to display with the DisplayAccountDetails method. Also, notice that in the MyStatistics class, we’re explicitly declaring the parameterless class constructor so that we can initialise new instances with some data.

There are a few other programming constructs to consider in the above class definitions so we’ll cover them now.

Initialisers

You may have noticed that the fields in the Account class had default values assigned to them (for example the account holder’s name defaulted to blank and the account balance to 0). These assignments are called initialisers as they provide an initial value for class members in the absence of explicit settings to help ensure that a new class instance can be initialised to a valid state.

Although class fields can have initialisers, this isn’t mandatory, as can be seen in the MyStatistics class. What is important to understand though is that uninitialised class-level fields must be initialised before the constructor returns otherwise a compile error is likely to occur. This differs for primitive (value) types where the compiler / CLR will typically just assign the default value (e.g. numeric types will be assigned 0, a Boolean would be assigned false, a character (char) would be assigned to null (escape code: \0), etc.).

So, only reference types need to be explicitly initialised in a class constructor (or by using an initialiser as part of the field declaration).

The ‘this’ Keyword

Within a class, you can use the this keyword to allow the class to reference itself (sometimes call just self). this becomes a hidden reference implicitly passed to all non-static (i.e. instance) members of a class.

The keyword can be used in a few different ways. One is like we have used in the Account class constructor above where we use the keyword to explicitly identify a class variable. You’ll notice that the class-level field names and the parameter names are the same. What would happen if we did this:

public Account(string holderName, int accountNumber, decimal balance)
{
    holderName = holderName;
    accountNumber = accountNumber;
    balance = balance;
}Code language: C# (cs)

The parameter would be assigned to itself in each statement. The class-level fields would not be updated at all. Typically, we’d use a leading underscore when naming class-level fields but we didn’t in this case specifically to highlight this issue.

// Using the 'this' keyword allows us to assign the class-level variable correctly.
this.holderName = holderName;

// If we had used Microsoft's default naming convention this wouldn't have been 
// an issue.
public class Account
{
    private string _holderName = "";

    public Account (string holderName)
    {
        // The 'this' keyword is optional as C# can uniquely identify _holderName 
        // as a field.
        _holderName = holderName;
    }
}Code language: C# (cs)

Another use for the this keyword is to pass a reference to the current object instance elsewhere. The most notable example of this is when raising events. Although events aren’t covered until a later tutorial, we can look at a code example here anyway.

// The standard 'EventHandler' prototype for an event is:
public delegate EventHandler(object sender, EventArgs e);

// Now we publish an event based on that signature:
public event EventHandler OnClick;

// The event is raised in another class:
OnClick.Invoke(this, new EventArgs());Code language: C# (cs)

A further use of the this keyword is when overloading constructors, to save repeating code in each constructor. For example:

public class Account
{
    private string _holderName = "";
    private int _accountNumber = 0;
    private decimal _balance = 0M;

    public Account(string holder, accountNumber)        
    {
        _holderName = holder;
        _accountNumber = accountNumber;        
    }

    public Account(string holder, int accountNUmber, decimal balance)
        : this(holder, accountNumber)
    {
        _balance = balance;
    }
}Code language: C# (cs)

In this case, we have two constructors, with a 2-parameter constructor for new accounts that accepts the holder name and account number and leaves the balance at zero. Instead of having to instantiate all 3 class-level fields in the 3-parameter constructor, we can have one constructor call the other and only need to intiailise the additional data requires in the more complex case. This reduces the amount of code we need to write.

A final use of the this keyword is to access and invoke class methods and properties. This isn’t, strictly speaking, required as the compiler and CLR treat method calls as localised to the class by default, but some developers prefer to include the this keyword for readability. For example:

public class Account
{
    // Code omitted for brevity.
 
    public void Credit(decimal amount)
    {
        _balance += amount;
    }

    public void Debit(decimal amount)
    {
        _balance -= amount;
    }

    public void HandleTransactions(decimal[] creditsAndDebits)
    {
        for (int i = 0; i < creditAndDebits.Length; i++)
        {
            if (creditAndDebits[i] < 0) this.Debit(creditAndDebits[i]);
            else if (creditAndDebits[i] > 0) this.Credit(creditAndDebits[i]);

            // But the above is equivalent to this commented out code which 
            // would also work fine...
            //if (creditAndDebits[i] < 0) Debit(creditAndDebits[i]);
            //else if (creditAndDebits[i] > 0) Credit(creditAndDebits[i]);
        }
    }
}Code language: C# (cs)

The ‘#region’ Pre-processor Directive

Before code is compiled, it goes through a process called pre-processing. The compiler’s pre-processor examines the code for any pre-processor directives, all of which begin with a #. These directives allow you to define identifiers and then test for their existence.

Here’s an example of using a pre-processor directive (DEBUG is a predefined directive set by the IDE):

#if DEBUG
    // Something only done if we're in debug build mode.
#endifCode language: C# (cs)

One of the built-in pre-processor directives that you may use a lot is the #region directive. This directive marks an area of code with a comment. The principle use of this pre-processor directive is to allow tools such as Visual Studio to mark off areas of code and collapse them in the editor with only the region title showing. This directive is actually skipped over by the pre-processor program and ignored so it is used by the IDE for code presentation purposes only. A complementary #endregion directive exists to mark the end of a region. Each #region directive must have a matching #endregion.

Most other directives (e.g. the #if directive) produce functional changes to the way the code is compiled.

When wrapped in a region directive a code region can be collapsed as shown in the screen-shots below:

C# region directive example

Static Members of a Class

The members of a class (the fields, properties, methods, etc.) can either be instance members or static members. An instance member is tied to an object instance of the class, where as a static member will be shared across all instances of that class.

You access a static member through the name of the class in which it is declared. For example, suppose you have a class named Counter, and have instantiated two objects of that type. You could define the Counter class as follows:

public class Counter
{
    private static int allCounts = 0;
    private int count = 0;

    public void Increment()
    {
        this.count++;
        Counter.allCounts++;
    }

    public void Decrement()
    {
        this.count--;
        Counter.allCounts--;
    }

    public void GetCount()
    {
        return this.count;
    }

    public void GetCountForAllInstances()
    {
        return Counter.allCounts;
    }
}Code language: C# (cs)

As you can see above, we define a static field and then access that static field in the class methods. Again we’ve purposely not followed Microsoft recommended naming best practice to demonstrate the improved readability of using the this prefix for instance fields and using the class name as a prefix for static fields.

If we tried this in any of our class methods, we’d get a compilation error:

this.allCounts++;Code language: C# (cs)

Some languages have a concept of class methods and global methods. C# does not make this distinction; there are only class methods in C#. Static methods act like global methods in that you can invoke them without needing to have an object instance of the class in hand.

The advantage here is that the name of the static declaration is scoped to the class in which it occurs, and therefore does not clutter any global namespace with a large amount of global definitions. The class acts like a namespace for the static methods it contains. It is possible, and may seem like a good idea, to code all of your miscellaneous methods in a single class, but this is not desirable and undermines the encapsulation of object orientated design.

Static Constructors

Classes can have a single static constructor that is guaranteed to run when the first instance of that class is created. Static constructors have no access modifiers and accept no parameters.

Static constructors, like any static method, cannot consume instance members. Static properties and methods can, however, create instances of classes and accept objects as parameters. 

These constructors don’t have to be used to initialise static fields, this can be done using an initializer, but they are useful for set up work that can’t be achieved with an initializer (for example configuring an object declared as a static field) that only needs to be done once.

In our Counter class example above, we might save the ‘allCounts’ value to a persisted data store periodically. If we then define a static constructor for the class that loads the previous value from the persisted state then we have a way to stop the all-counts from zeroing again each time the program runs.

Static Classes

As previously mentioned, there are no global methods or constants in C#. You may find yourself creating small utility classes that just hold static members. If you create such a class, you may not want any instances of it being created. Marking your class with the static keyword ensures that your class cannot be instantiated. Also note that static classes do not support inheritance.

It may not be immediately obvious why it is worth having a static class at all, but it can be useful to separate and ring-fence shared functionality or definitions from per-instance scopes and for this it can be very useful. For example you might create a StringUtils static class containing shared methods to provide extended processing and manipulation of strings.

Static Fields

We briefly hinted about static fields in the static constructors section above. Any static fields defined in a class are accessible and shared amongst all instances of that class.

Let’s revisit the Account class we were looking at earlier. We could modify/enhance it like this:

public class Account
{
    private static int s_numberOfAccounts = 0;
    private string _holderName = "";
    private int _accountNumber = 0;
    private decimal _balance = 0M;

    public Account(string holder, accountNumber)        
    {
        _holderName = holder;
        _accountNumber = accountNumber; 

        s_numberOfAccounts++;       
    }

    public Account(string holder, int accountNUmber, decimal balance)
        : this(holder, accountNumber)
    {
        _balance = balance;
    }

    public void DisplayAccountDetails()
    {
        Console.WriteLine(
            "{0} holds Account: {1}, with a balance of {2}",
            _holderName, _accountNumber, _balance.ToString("C"));
    }

    public void DisplayTotalNumberOfAccounts()
    {
        Console.WriteLine("There are a total of {0} accounts.", s_numberOfAccounts);
    }

    public static int GetTotalNumberOfAccounts()
    {
        return s_numberOfAccounts;
    }
}Code language: C# (cs)

In this example, we have a static variable that we’ve defined called s_numberOfAccounts (Microsoft’s recommended naming convention for static fields is to prepend an ‘s’ before the underscore to differentiate between a static field and an instance field with just a leading underscore). The static field is initialised to 0. Every time a new instance of the class is created the static field is incremented.

This obviously isn’t a particularly good design because Account class instances may be created and destroyed with the same account number multiple times, but in our case we are therefore specifying that we’re only creating each account instance object once, and only once, for simplicity.

Data Encapsulation Using Properties

In C#, it is considered bad practice to make data fields public. This applies to static fields as well as instance ones. Member data should be declared as private, and then retrieved using an ‘accessor’ method for consumption by the caller. In the above example, we used a traditional accessor GetTotalNumberOfAccounts() to access and return the value of the s_numberOfAccounts field.

This isn’t best practice either. Field data would normally be encapsulated by Properties.

In contrast to traditional accessor and mutator methods (GetSomething(), SetSomething(int value)), .NET languages prefer to enforce encapsulation using Properties which simulate publicly accessible points of data. Rather than requiring the client class to use two different methods to get and set the state of a data field, the developer is able to access what appears to be a public field. For example, let’s suppose you wanted to access the account balance in our account class example. Adding the following code to our class defines the property to do just that:

private decimal _balance = 0M;

public decimal Balance
{
    get 
    {
        return _balance;
    }
    set 
    {
        _balance = value;
    }
}Code language: C# (cs)

Immediately above is the most historical way of defining a property in C#. From .NET 3.5 (C# 3.0) onwards we have been able to declare ‘auto-properties’ without the need for any ‘backing field’ using a shorthand style, like this:

public decimal Balance { get; set; }Code language: C# (cs)

A drawback of this approach is that it doesn’t support initialisers, so you still need to explicitly set your initial property values in the class constructor(s). For primitive (value) types the default value will be used, so you only need an explicit assignment in the constructor for reference types or where the default value isn’t suitable.

UPDATE: a few years after this article was originally written, Microsoft introduced initialisers for auto-properties with C# 6.0. Yay!

The property is composed of a get block (the accessor) and a set block (the mutator).

Properties’ get and set code blocks always map onto accessor and mutator methods under the covers. Buried within the compiled intermediate (MSIL) code, the property will have been converted into two methods, called get_Balance and set_Balance. You don’t need to worry about this, but it is good to know in case you are doing anything complicated (e.g. trying to access or update property values using .NET Reflection).

If you use the traditional declaration style (with a backing-field and explicit get and set code blocks) then you are able to perform any internal logic necessary before returning the current value or making the value assignment, for example checking it is within a valid range, coercing it if necessary, and so on. The value keyword is simply the parameter passed to the behind the scenes mutator method, and represents the data value you are setting, the type of which is constrained in the property declaration.

Accessing the property is simple:

decimal currentBalance = myAccount.Balance;

// OR, setting the property...

myAccount.Balance = 12759.12M;Code language: C# (cs)

At this point you may be concerned that you’ve just made the balance value writable outside of the class. Well, in our example that is indeed the case but you can declare properties with access modifiers on the get and set keywords, so we could re-write our property declaration using the shorthand syntax like this:

public decimal Balance { get; private set; }

// OR, using protected instead...

public decimal Balance { get; protected set; }Code language: C# (cs)

Now the property appears to be read-only external to the class, but the class itself can update it as necessary. If it isn’t immediately obvious why protected might be a good choice here, it is because that makes the property writable in derived classes where this may be required.

When declaring properties, if you use the traditional style, then you can defined them as read-only or write-only. For example:

private int _count = 0;

public int Count
{
    return _count;
}

private string _processName = "";

public string ProcessName
{
    set 
    {
        _processName = value;
    }
}Code language: C# (cs)

Destroying Object Instances

The CLR provides garbage collection services, automatically cleaning up managed objectswhen they are no longer in use. You typically don’t need to do anything for this to happen – it just does it behind the scenes.

If, however, your class controls unmanaged resources then you are responsible for explicitly freeing those resources when the object is being disposed of. C# supports the destructor pattern, so you can declare a destructor in your class to manage any explicit cleanup it needs to perform.

The format of a destructor is a tilde followed by the class name, for example:

~Account()
{
    // TODO: release those unmanaged resources.
}Code language: C# (cs)

.NET supports another way of disposing of object resources when it is being destroyed but that relies on knowledge of interfaces which we’ll learn about in a later tutorial, so we can revisit this topic then.

Passing Parameters in Methods

We’ve already used method parameters in a lot of example code so far, but it is worth looking into how passing of data and object in parameter works as it can affect how your code executes.

By default, value types are passed into methods ‘by value’. This means that when a value is passed to a method, a temporary copy of the value is created within that method. Although passing by value is the normal case, there are times where you need to pass a value by reference. C# provides the ref keyword as a parameter modifier for passing these values by reference instead.

public void Add(decimal current, decimal amount)
{
    current += amount;
}

public void AddByRef(ref decimal current, decimal amount)
{
    current += amount;
}

public void DoCalculation()
{
    decimal total = 1583.12M;

    Add(total, 4.08M);

    // What is the value stored by the total variable at this point? (1).

    AddByRef(total, 4.08M);

    // What is the value stored by 'total' now? (2).
} 
Code language: C# (cs)

In the above example, what do you think the total variable is assigned to at points (1) and (2)?

It may surprise you to know that at point (1), total still equals 1583.12. This is because a copy of total and not the variable itself was passed to the method. Conversely, at point (2) total is 1587.20 as expected, because the variable was passed ‘by reference’ so the same variable (memory pointer/location) was used both outside and inside the method call.

Instead of passing the variable by reference, we could modify the Add method above to return the new total instead, to get around the problem we observed like this:

public decimal Add(decimal current, decimal amount)
{
    return current + amount;
}

// When calling the method we would do this instead...
total = Add(total, 4.08M);Code language: C# (cs)

Reference types are always passed by reference, and this is equally important to understand because passing the variable as a parameter in that case could have unforeseen side effects if the method modifies it unexpectedly.

C#’s string data type is an exception to the reference types rule in this regard. Although it is a reference type, C# strings are immutable, meaning a new memory reference is created each time they are modified. We’ll discuss strings in more detail in a later tutorial but I wanted to make you aware that strings may behave differently to how you might expect them to because of this.

Method Overloading

Method overloading describes a mechanism by which methods in a class might have the same name but they each have different method ‘signatures’. Consider the following:

bool Validate(string name) { }
bool Validate(string name, DateTime dateOfBirth) { }
bool Validate(string name, DateTime dateOfBirth, string jobTitle) { }
bool Validate(string name, DateTime timeInRole) { } // Illegal!Code language: C# (cs)

The above code declares 4 methods witin a class, each with the same name and only differing by signature. The last of the 4 methods would actually be illegal and result in a compiler error as there is already another method with the same signature as it (the second method).

If it isn’t immediately obvious, the signature of a method is defined by its name and the data types of the parameters it receives. In the above case we had two methods is the same signature of Validate(string, DateTime), so the last one was illegal.

Overloaded methods can even call each other (although you should use caution not to create infinite loops by mistake if you are doing this). For example, the Validate(string) overload may validate just the name value in a dataset, and the Validate(string, DateTime) overload could call the name only version to validate just the name and the validate the date of birth itself. Again, the most complex overload (Validate(string, DateTime, string)) could call the ‘name + date of birth’ overload to validate those two values and simply validate the job title value afterwards.

We’ve already demonstrated above how constructor overloading works in our code examples relating to class constructors, the this keyword, etc. so we won’t go over all that again here.

Structs

A struct or ‘structure’ is a simple, light-weight user defined type that is an alternative to a class. Structs are like classes in that they can contain methods, properties, indexers, constructors, etc. but the main difference is that classes are reference types and structs are value types. Structs are therefore good for describing types that don’t require reference semantics and are relatively small in instance size. Structs don’t allow destructors, nor do they allow inheritance.

Structs are declared in much the same way as a class, except the struct keyword is used instead. For example:

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

namespace CreatingStructs
{
    public struct PaymentRecord
    {
        public decimal amount;
        public DateTime date;

        public PaymentRecord(decimal amount, DateTime date)
        {
            this.amount = amount;
            this.date = date;
        }

        public decimal Amount
        {
            get 
            {
                return this.amount; 
            }
            set 
            {
                this.amount = value;
            }
        }

        public DateTime Date
        {
            get 
            {
                return this.date; 
            }
            set 
            { 
                this.date = value; 
            }
        }

        public override string ToString()
        {
            return string.Format("{0} on {1}",
                this.amount.ToString("C"), this.date.ToShortDateString());
        }
    }
}
Code language: C# (cs)

Some things to note above:

  • The fields cannot have initialisers. The field value must be initialised by the struct‘s constructor.
  • A struct can utilise other structs (like DateTime in our example case here).
  • A struct can have properties, just like a class.
  • A struct can have methods, just like a class.
  • Notice we’ve sneakily introduced a new keyword here too (override). In this case, the default ToString() is being overridden to give a custom string representation of the struct instead of the default which typically just returns the type’s name.

Enumerated Types

In addition to structs, the enumeration is another member of the .NET value-types category.

When you build a program, it is often convenient to create a set of symbolic names for underlying numerical values. For example, if you were creating a database-based employee payroll system, you may want to use the constants for Manager, Worker, Contractor and VP rather than simple numerical values 0, 1, 2 and 3 respectively. (The reason you might assign roles as numeric values is they take up less storage in the database than the text values would.)

The trouble is that defining the values as constants doesn’t necessarily group and co-locate them very well. Instead, you can define them as an enumerated type like so:

public enum EmployeeType
{
    Manager,      // = 0
    Worker,       // = 1
    Contractor,   // = 2
    VP            // = 3
}Code language: C# (cs)

The default underlying data type for an enumerated type is an integer (int). If you don’t explicitly assign values to your enumerated type names they will increment as declared from 0 to N. The type can be overridden if you have the need for it, explicitly declaring it to be one of the primitive numeric integer types (as that is all C# supports). For example:

public enum EmployeeType : byte
{
    // TODO: add value names here.
}Code language: C# (cs)

There is one further configuration option to mention and that is something we once again haven’t mentioned until now, and that’s attributes. We won’t go into much detail here, except to say .NET attributes allow you to decorate other declarations and enhance or modify the behaviour of them. The attirbute we are interested in here, is the Flags attribute.

If we modify the above enumerated type definition to that shown below, then the number values will no longer increment by one each time. Also, in such situations we usually switch the enum name from the singular to the plural, for example:

[Flags]
public enum EmployeeTypes : byte
{
    NotSet,       // = 0  (00000000)
    Manager,      // = 1  (00000001)
    Worker,       // = 2  (00000010)
    Contractor,   // = 4  (00000100)
    VP,           // = 8  (00001000)
    CEO           // = 16 (00010000)
}Code language: C# (cs)

What the Flags attribute tells the compiler is that the enumeration values are bit masked. The advantage of bit masking is that it allows multiple values to be logically ANDed together in an assignment and for a variable to be evaluated against each enumeration value if required. For example:

EmployeeTypes myRoles = EmployeeTypes.Manager & EmployeeTypes.Worker;

if ((myRoles & EmployeeTypes.Manager) == EmployeeTypes.Manager)
{
    // The employee is a manager so let them see the management screens.
}Code language: PHP (php)

In the above code the myRoles variables was assigned two employee types. The user is acting as both a manager and a worker in this case. If you tried to evaluate the variable against a single enumeration value (e.g. if (myRoles == EmployeeTypes.Manager)) then the expression would fail and return false. So, instead we can logically AND the myRoles variable with the employee type we are interested in and it will return the employee type value (EmployeeTypes.Manager in this case) if it works, or 0 otherwise.