dimanche 28 juillet 2013

Lesson 23: Working with Nullable Types

Working with value types and data can sometimes be challenging because a value type doesn't normally hold a null value. This lesson shows you how to overcome this limitation with C# nullable types. Here's what you'll learn.
  • Understand the problem that nullable types solve
  • See how to declare a nullable type
  • Learn how to use nullable types

Understanding the Problem with Value Types and Null Values

As explained in Lesson 12: Structs, the default value of a struct (value type) is some form of 0. This is another difference between reference types and value types, in addition to what was described in Lesson 22: Topics on C# Type. The default value of a reference type is null. If you're just writing C# code and managing your own data source, such as a file that holds data for your application, the default values for structs works fine.
In reality, most applications work with databases, which have their own type systems. The implications of working with database type systems is that you don't have a one-to-one mapping between C# and database types. One glaring difference is that database types can be set to null. A database has no knowledge of reference and value types, which are C# language (.NET Platform) concepts. This means that C# value type equivalents in the database, such as int, decimal, and DateTime, can be set to null.
Since a type in the database can be null, but your C# value type can't be null, you have to find some way to provide a translation in your C# code to account for null values. Effectively, the scheme you use will often be inconsistent from one program to another; something you often don't have a choice about. For example, what if you wanted to handle a null DateTime from SQL Server as the minimum DateTime value in C#. After that project, your next task would be to read data from a legacy Foxpro database, whose minimum DateTime value is different from SQL Server. Because of this lack of constency and potential confusion, C# 2.0 added nullable types, which are more elegant and natural for working with null data.

Declaring Nullable Types

To declare a value type as nullable, append a question mark, ?, to the type name. Here's how to declare a DateTime variable as a nullable type:
    DateTime? startDate;
A DateTime can't normally hold a null value, but the declaration above enables startDate to hold null, as well as any legal DateTime value. The proper terminology is to refer to the type of startDate as a nullable DateTime.
You can assign a normal value to startDate like this:
    startDate = DateTime.Now;
or you can assign null, like this:
    startDate = null;
Here's another example that declares and initializes a nullable int:
    int? unitsInStock = 5;
The unitsInStock in the example above can be assigned a value of null also.

Working with Nullable Types

When you have nullable types, you'll want to check them to see if they're null. Here's an example that shows how you can check for a null value:
    bool isNull = startDate == null;

    Console.WriteLine("isNull: " + isNull);
The example above shows that you only need to use the equals operator to check for null. You could also make the equality check as part of an if statement, like this:
    int availableUnits;

    if (unitsInStock == null)
    {
        availableUnits = 0;
    }
    else
    {
        availableUnits = (int)unitsInStock;
    }
Note: Notice the cast operator in the else clause above. An explicit conversion is required when assigning from nullable to non-nullable types.
That's several lines of code for something that appears to be such a common operation. Fortunately, there's a better way to perform the same task, using the coalesce operator, ??, shown below:
    int availableUnits = unitsInStock ?? 0;
The coalesce operator works like this: if the first value (left hand side) is null, then C# evaluates the second expression (right hand side).

Summary

This lesson explained how nullable types can be useful in your C# applications - especially when working with values from a database. You learned how to declare a nullable type and how to assign values, both null and non-null to nullable types. Another skill you learned was how to use nullable types by checking to see if their values are null. As you saw, the coalesce operator can be useful to help work with nullable type variables when you need to assign a valid value to a non-nullable type.

Lesson 22: Topics on C# Type

Throughout this tutorial, you've seen many different types, including those that are part of C# and custom designed types. If you've taken the samples and worked on them yourself, extending and writing your own programs, you are likely to have experienced errors associated with type. For example, you can't assign a double to an int without using a cast operator to perform the conversion. Another feature of C# concerns the semantic differences between reference and value types. Such problems should make you wonder why this is so and that's what this lesson is for. Here are the objectives for this lesson:
  • Understand the need for type safety
  • See how to convert one type to another
  • Learn about reference types
  • Learn about value types
  • Comprehend the semantic differences between reference and value types

Why Type Safety?

In untyped languages, such as scripting languages, you can assign one variable to another and the compiler/interpreter will use an intelligent algorithm to figure out how the assignment should be done. If the assignment is between two variables of the same type, all is good. However, if the assignment is between different types, you could have serious problems.
For example, if you assigned an int value to a float variable it would convert okay because the fractional part of the new float would just be zero. However, if you went the other way and assigned a float value to an int variable, that would most likely be a problem. You would lose all of the precision of the original float value. Consider the damage that could be caused if the float value represented a chemical ingredient, an engineering measurement, or a financial value. Finding such an error would be difficult and particularly expensive, especially if the error didn't show up until your application was in production (already being used by customers).

Using the Cast Operator for Conversions

In Lesson 02: Operators, Types, and Variables, you learned about C# types and operators. It explained the size and precision of the various types and there is a list of available operators. The cast operator, (x), is listed first as a primary operator in Table 2-4. When you must convert a type that doesn't fit, it must be done via what is called an explicit conversion, which uses the cast operator. Listing 22-1 has an example of an implicit conversion, which doesn't require the cast operator, and an explicit conversion.
Listing 22-1. Cast Operators
using System;

class Program
{
    static void Main()
    {
        float lengthFloat = 7.35f;

        // lose precision - explicit conversion
        int lengthInt = (int)lengthFloat;

        // no problem - implicit conversion
        double lengthDouble = lengthInt;

        Console.WriteLine("lengthInt = " + lengthInt);
        Console.WriteLine("lengthDouble = " + lengthDouble);
        Console.ReadKey();
    }
}
Here's the output:
lengthInt = 7
lengthDouble = 7
Since a float, lengthFloat, has a fractional part but an int, lengthInt, doesn't; the types aren't compatible. Because of type safety, C# won't allow you to assign lengthFloat directly to lengthInt, which would be dangerous. For your protection, you must use a cast operator, (int), to force the explicit conversion of lengthFloat to lengthInt. In the output, you can see that lengthInt is 7, showing that it lost the fractional part of the 7.35f value from lengthFloat.
The assignment from lengthInt to lengthDouble is safe because a double is 64-bit and an int is 32-bit, meaning that you won't lose information. Therefore, the conversion is implicit, meaning that you can perform the assignment without the cast operator.

Understanding Reference Types

Reference type variables are named appropriately (reference) because the variable holds a reference to an object. In C and C++, you have something similar that is called a pointer, which points to an object. While you can modify a pointer, you can't modify the value of a reference - it simply points at the object in memory.
An important fact you need to understand is that when you are assigning one reference type variable to another, only the reference is copied, not the object. The variable holds the reference and that is what is being copied. Listing 22-2 shows how this works.
Listing 22-2. Reference Type Assignment
using System;

class Employee
{
    private string m_name;

    public string Name
    {
        get { return m_name; }
        set { m_name = value; }
    }
}

class Program
{
    static void Main()
    {
        Employee joe = new Employee();
        joe.Name = "Joe";

        Employee bob = new Employee();
        bob.Name = "Bob";

        Console.WriteLine("Original Employee Values:");
        Console.WriteLine("joe = " + joe.Name);
        Console.WriteLine("bob = " + bob.Name);

        // assign joe reference to bob variable
        bob = joe;

        Console.WriteLine();
        Console.WriteLine("Values After Reference Assignment:");
        Console.WriteLine("joe = " + joe.Name);
        Console.WriteLine("bob = " + bob.Name);

        joe.Name = "Bobbi Jo";

        Console.WriteLine();
        Console.WriteLine("Values After Changing One Instance:");
        Console.WriteLine("joe = " + joe.Name);
        Console.WriteLine("bob = " + bob.Name);

        Console.ReadKey();
    }
}
Here's the output:
Original Employee Values:
joe = Joe
bob = Bob

Values After Reference Assignment:
joe = Joe
bob = Joe

Values After Changing One Instance:
joe = Bobbi Jo
bob = Bobbi Jo
In Listing 22-2, I created two Employee instances, joe and bob. You can see in the output that the Name properties of both Employee instances each show their assigned values from when the objects were first created. After assigning joe to bob, the value of the Name properties of both instances are the same. This is what you might expect to see.
What might surprise you is the values that occur after assigning a value to the Employee instance variable named joe. If you look at the code closely, you'll notice that it doesn't change bob - only Joe. However, the results from the output show that the Name property in bob is the same as the Name property in joe. This demonstrates that after assigning joe to bob, both variables held references to the joe object. Only the reference was copied - not the object. This is why you see the results of printing Name in both joe and bob are the same because the change was on the object that they both refer to.
The following types are reference types:
  • arrays
  • class'
  • delegates
  • interfaces
Note: The primitive type, string, is also a reference type.

Understanding Value Types

Value type variables, as their name (value) suggests, hold the object value. A value type variable holds its own copy of an object and when you perform assignment from one value type variable to another, both the left-hand-side and right-hand-side of the assignment hold two separate copies of that value. Listing 22-3 shows how value type assignment works.
Listing 22-3. Value Type Assignment
using System;

struct Height
{
    private int m_inches;

    public int Inches
    {
        get { return m_inches; }
        set { m_inches = value; }
    }
}

class Program
{
    static void Main()
    {
        Height joe = new Height();
        joe.Inches = 71;

        Height bob = new Height();
        bob.Inches = 59;

        Console.WriteLine("Original Height Values:");
        Console.WriteLine("joe = " + joe.Inches);
        Console.WriteLine("bob = " + bob.Inches);

        // assign joe value to bob variable
        bob = joe;

        Console.WriteLine();
        Console.WriteLine("Values After Value Assignment:");
        Console.WriteLine("joe = " + joe.Inches);
        Console.WriteLine("bob = " + bob.Inches);

        joe.Inches = 65;

        Console.WriteLine();
        Console.WriteLine("Values After Changing One Instance:");
        Console.WriteLine("joe = " + joe.Inches);
        Console.WriteLine("bob = " + bob.Inches);

        Console.ReadKey();
    }
}
Here's the output:
Original Height Values: 
joe = 71 
bob = 59 

Values After Value Assignment: 
joe = 71 
bob = 71 

Values After Changing One Instance: 
joe = 65 
bob = 71
In Listing 22-3, you can see that the Inches property of bob and joe are initially set to different values. After assigning joe to bob, a value copy occurs, where both of the variables have the same value, but are two separate copies. To demonstrate value assignment results, notice what happens after setting joe to 65; The output shows that bob did not change, which demonstrates that value types hold distinct copies of their objects.
The following types are value types:
  • enum
  • struct
Note: All of the primitive types (int, char, double, etc.), except for string, are value types.

Reference Type and Value Type Differences

From the previous paragraphs, you might already see that there is a difference reference type and value type assignment. Reference types copy a reference to an object and value types copy the object. If you don't know this, then the effects can be surprising in your code when performing tasks such as making assignments and passing arguments to methods.

Summary

This lesson provided a few tips on working with types in C#. You should now have a better understanding of type safety and how it can help you avoid problems. This lesson showed you how to use a cast operator to perform conversions and explained the difference between explicit and implicit conversions. You also know that the type system is divided between reference types and value types. To demonstrate the differences between reference types and value types, this lesson provided examples that showed how both reference types and value types behave during assignment.
I invite you to return for Lesson 23: Working with Nullable Types.

Lesson 21: Anonymous Methods

In Lesson 14: Introduction to Delegates, you learned about delegates and how they enable you to connect handlers to events. For C# v2.0, there is a new language feature, called anonymous methods, that are similar to delegates, but require less code. While you learn about anonymous methods, we'll cover the following objectives:
  • Understand the benefits of anonymous methods
  • Learn how to implement an anonymous method
  • Implement anonymous methods that use delegate parameters

How Do Anonymous Methods Benefit Me?

An anonymous method is a method without a name - which is why it is called anonymous. You don't declare anonymous methods like regular methods. Instead they get hooked up directly to events. You'll see a code example shortly.
To see the benefit of anonymous methods, you need to look at how they improve your development experience over using delegates. Think about all of the moving pieces there are with using delegates: you declare the delegate, write a method with a signature defined by the delegate interface, declare the event based on that delegate, and then write code to hook the handler method up to the delegate. With all this work to do, no wonder programmers, who are new to C# delegates, have to do a double-take to understand how they work.
Because you can hook an anonymous method up to an event directly, a couple of the steps of working with delegates can be removed. The next section shows you how this works.

Implementing an Anonymous Method

An anonymous method uses the keyword, delegate, instead of a method name. This is followed by the body of the method. Typical usage of an anonymous method is to assign it to an event. Listing 21-1 shows how this works.
Listing 21-1. Implementing an Anonymous Method
using System.Windows.Forms;

public partial class Form1 : Form
{
    public Form1()
    {
        Button btnHello = new Button();
        btnHello.Text = "Hello";

        btnHello.Click +=
            delegate
            {
                MessageBox.Show("Hello");
            };

        Controls.Add(btnHello);
    }
}
The code in Listing 21-1 is a Windows Forms application. It instantiates a Button control and sets its Text to "Hello". Notice the combine, +=, syntax being used to hook up the anonymous method. You can tell that it is an anonymous method because it uses the delegate keyword, followed by the method body in curly braces.
Essentially, you have defined a method inside of a method, but the body of the anonymous method doesn't execute with the rest of the code. Because you hook it up to the event, the anonymous method doesn't execute until the Click event is raised. When you run the program and click the Hello button, you'll see a message box that say's "Hello" - courtesy of the anonymous method.
Using Controls.Add, adds the new button control to the window. Otherwise the window wouldn't know anything about the Button and you wouldn't see the button when the program runs.

Using Delegate Parameters with Anonymous Methods

Many event handlers need to use the parameters of the delegate they are based on. The previous example didn't use those parameters, so it was more convenient to not declare them, which C# allows. Listing 21-2 shows you how to use parameters if you need to.
Listing 21-2. Using Parameters with Anonymous Methods
using System;
using System.Windows.Forms;

public partial class Form1 : Form
{
    public Form1()
    {
        Button btnHello = new Button();
        btnHello.Text = "Hello";

        btnHello.Click +=
            delegate
            {
                MessageBox.Show("Hello");
            };

        Button btnGoodBye = new Button(); 
        btnGoodBye.Text = "Goodbye";
        btnGoodBye.Left = btnHello.Width + 5; 
        btnGoodBye.Click += 
            delegate(object sender, EventArgs e) 
            { 
                string message = (sender as Button).Text;
                MessageBox.Show(message); 
            };

        Controls.Add(btnHello);
        Controls.Add(btnGoodBye);
    }
}
The bold parts of Listing 21-2 show another Button control added to the code from Listing 21-1. Besides changing the text, btnGoodBye is moved to the right of btnHello by setting it's Left property to 5 pixels beyond the right edge of btnHello. If we didn't do this, btnGoodBye would cover btnHello because both of their Top and Left properties would default to 0.
Beyond implementation details, the real code for you to pay attention to is the implementation of the anonymous method. Notice that the delegate keyword now has a parameter list. this parameter list must match the delegate type of the event that the anonymous method is being hooked up to. The delegate type of the Click event is EventHandler, which has the following signature:
public delegate void EventHandler(object sender, EventArgs e);
Notice the EventHandler parameters. Now, here's how the Button control's Click event is defined:
public event EventHandler Click;
Notice that the delegate type of the Click event is EventHandler. This is why the anonymous method, assigned to btnGoodBye.Click in Listing 21-2, must have the same parameters as the EventHandler delegate.

Summary

Anonymous methods are a simplified way for you to assign handlers to events. They take less effort than delegates and are closer to the event they are associated with. You have the choice of either declaring the anonymous method with no parameters or you can declare the parameters if you need them.
I invite you to return for Lesson 22: Topics on C# Type.

Lesson 20: Introduction to Generic Collections

All the way back in Lesson 02, you learned about arrays and how they allow you to add and retrieve a collection of objects. Arrays are good for many tasks, but C# v2.0 introduced a new feature called generics. Among many benefits, one huge benefit is that generics allow us to create collections that allow us to do more than allowed by an array. This lesson will introduce you to generic collections and how they can be used. Here are the objectives for this lesson:
  • Understand how generic collections can benefit you
  • Learn how to create and use a generic List
  • Write code that implements a generic Dictionary

What Can Generics Do For Me?

Throughout this tutorial, you've learned about types, whether built-in (int, float, char) or custom (Shape, Customer, Account). In .NET v1.0 there were collections, such as the ArrayList for working with groups of objects. An ArrayList is much like an array, except it could automatically grow and offered many convenience methods that arrays don't have. The problem with ArrayList and all the other .NET v1.0 collections is that they operate on type object. Since all objects derive from the object type, you can assign anything to an ArrayList. The problem with this is that you incur performance overhead converting value type objects to and from the object type and a single ArrayList could accidentally hold different types, which would cause hard to find errors at runtime because you wrote code to work with one type. Generic collections fix these problems.
A generic collection is strongly typed (type safe), meaning that you can only put one type of object into it. This eliminates type mismatches at runtime. Another benefit of type safety is that performance is better with value type objects because they don't incur overhead of being converted to and from type object. With generic collections, you have the best of all worlds because they are strongly typed, like arrays, and you have the additional functionality, like ArrayList and other non-generic collections, without the problems.
The next section will show you how to use a generic List collection.

Creating Generic List<T> Collections

The pattern for using a generic List collection is similar to arrays. You declare the List, populate its members, then access the members. Here's a code example of how to use a List:
    List<int> myInts = new List<int>();

    myInts.Add(1);
    myInts.Add(2);
    myInts.Add(3);

    for (int i = 0; i < myInts.Count; i++)
    {
        Console.WriteLine("MyInts: {0}", myInts[i]);
    }
The first thing you should notice is the generic collection List<int>, which is referred to as List of int. If you looked in the documentation for this class, you would find that it is defined as List<T>, where T could be any type. For example, if you wanted the list to work on string or Customer objects, you could define them as List<string> or List<Customer> and they would hold only string or Customer objects. In the example above, myInts holds only type int.
Using the Add method, you can add as many int objects to the collection as you want. This is different from arrays, which have a fixed size. The List<T> class has many more methods you can use, such as Contains, Remove, and more.
There are two parts of the for loop that you need to know about. First, the condition uses the Count property of myInts. This is another difference between collections and arrays in that an array uses a Length property for the same thing. Next, the way to read from a specific position in the List<T> collection, myInts[i], is the exact same syntax you use with arrays.
The next time you start to use a single-dimension array, consider using a List<T> instead. That said, be sure to let your solution fit the problem and use the best tool for the job. i.e. it's common to work with byte[] in many places in the .NET Framework.

Working with Dictionary<TKey, TValue> Collections

Another very useful generic collection is the Dictionary, which works with key/value pairs. There is a non-generic collection, called a Hashtable that does the same thing, except that it operates on type object. However, as explained earlier in this lesson, you want to avoid the non-generic collections and use thier generic counterparts instead. The scenario I'll use for this example is that you have a list of Customers that you need to work with. It would be natural to keep track of these Customers via their CustomerID. The Dictionary example will work with instances of the following Customer class:
    public class Customer
    {
        public Customer(int id, string name)
        {
            ID = id;
            Name = name;
        }

        private int m_id;

        public int ID
        {
            get { return m_id; }
            set { m_id = value; }
        }

        private string m_name;

        public string Name
        {
            get { return m_name; }
            set { m_name = value; }
        }
    }
The Customer class above has a constructor to make it easier to initialize. It also exposes its state via public properties. It isn't very sophisticated at this point, but that's okay because its only purpose is to help you learn how to use a Dictionary collection.  The following example populates a Dictionary collection with Customer objects and then shows you how to extract entries from the Dictionary:
     Dictionary<int, Customer> customers = new Dictionary<int, Customer>();

    Customer cust1 = new Customer(1, "Cust 1");
    Customer cust2 = new Customer(2, "Cust 2");
    Customer cust3 = new Customer(3, "Cust 3");

    customers.Add(cust1.ID, cust1);
    customers.Add(cust2.ID, cust2);
    customers.Add(cust3.ID, cust3);

    foreach (KeyValuePair<int, Customer> custKeyVal in customers)
    {
        Console.WriteLine(
            "Customer ID: {0}, Name: {1}",
            custKeyVal.Key,
            custKeyVal.Value.Name);
    }
The customers variable is declared as a Dictionary<int, Customer>.  Considering that the formal declaration of Dictionary is Dictionary<TKey, TValue>, the meaning of customers is that it is a Dictionary where the key is type int and the value is type Customer. Therefore, any time you add an entry to the Dictionary, you must provide the key because it is also the key that you will use to extract a specified Customer from the Dictionary.
I created three Customer objects, giving each an ID and a Name. I'll use the ID as the key and the entire Customer object as the value. You can see this in the calls to Add, where custX.ID is added as the key (first parameter) and the custX instance is added as the value (second parameter).
Extracting information from a Dictionary is a little bit different. Iterating through the customers Dictionary with a foreach loop, the type returned is KeyValuePair<TKey, TValue>, where TKey is type int and TValue is type Customer because those are the types that the customers Dictionary is defined with.
Since custKeyVal is type KeyValuePair<int, Customer> it has Key and Value properties for you to read from. In our example, custKeyVal.Key will hold the ID for the Customer instance and custKeyVal.Value will hold the whole Customer instance. The parameters in the Console.WriteLine statement demonstrate this by printing out the ID, obtained through the Key property, and the Name, obtained through the Name property of the Customer instance that is returned by the Value property.
The Dictionary type is handy for those situations where you need to keep track of objects via some unique identifier. For your convenience, here's Listing 20-1, shows how both the List and Dictionary collections work.
Listing 20-1. Introduction to Using Generic Collections with an Example of the List<T> and Dictionary<TKey, TValue> Generic Collections
using System;
using System.Collections.Generic;

public class Customer
{
    public Customer(int id, string name)
    {
        ID = id;
        Name = name;
    }

    private int m_id;

    public int ID
    {
        get { return m_id; }
        set { m_id = value; }
    }

    private string m_name;

    public string Name
    {
        get { return m_name; }
        set { m_name = value; }
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<int> myInts = new List<int>();

        myInts.Add(1);
        myInts.Add(2);
        myInts.Add(3);

        for (int i = 0; i < myInts.Count; i++)
        {
            Console.WriteLine("MyInts: {0}", myInts[i]);
        }

        Dictionary<int, Customer> customers = new Dictionary<int, Customer>();

        Customer cust1 = new Customer(1, "Cust 1");
        Customer cust2 = new Customer(2, "Cust 2");
        Customer cust3 = new Customer(3, "Cust 3");

        customers.Add(cust1.ID, cust1);
        customers.Add(cust2.ID, cust2);
        customers.Add(cust3.ID, cust3);

        foreach (KeyValuePair<int, Customer> custKeyVal in customers)
        {
            Console.WriteLine(
                "Customer ID: {0}, Name: {1}",
                custKeyVal.Key,
                custKeyVal.Value.Name);
        }

        Console.ReadKey();
    }
}
Whenever coding with the generic collections, add a using System.Collections.Generic declaration to your file, just as in Listing 20-1.

Summary

Generic collections give you the best of all worlds with the strong typing of arrays and flexibility of non-generic collections. There are many more generic collections to choose from also, such as Stack, Queue, and SortedDictionary. Look in the System.Collections.Generic namespace for other generic collections.
I invite you to return for Lesson 21: Anonymous Methods.

Lesson 19: Encapsulation

Earlier in this tutorial, you learned about two of the important principles of object-oriented programming, Inheritance and Polymorphism. Now that you've seen much of the syntax of C#, I'll show you how C# supports the another of the object-oriented principles - Encapsulation. This lesson will discuss Encapsulation with the following objectives:
  • Understand the object-oriented principle of Encapsulation.
  • Learn the available modifiers for type members.
  • Protect object state through properties.
  • Control access to methods.
  • Learn how to modify types for assembly encapsulation

What is Encapsulation and How Does It Benefit Me?

In object-oriented programming, you create objects that have state and behavior. An object's state is the data or information it contains. For example, if you have a BankAccount object, its state could be Amount and CustomerName. Behavior in an object is often represented by methods. For example, the BankAccount object's behavior could be Credit, Debit, and GetAmount. This sounds like a nice definition of an object, and it is, but you must also consider how this object will be used.
When designing an object, you must think about how others could use it. In a best-case scenario any program using the object would be well designed and the code would never change. However, the reality is that programs do change often and in a team environment many people touch the same code at one time or another. Therefore, it is beneficial to consider what could go wrong as well as the pristine image of how the object *should* be used.
In the case of the BankAccount object, examine the situation where code outside of your object could access a decimal Amount field or a string CustomerName field. At the point of time that the code is written, everything would work well. However, later in the development cycle, you realize that the BankAccount object should keep track of an int CustomerID rather than string CustomerName because you don't want to duplicate relationships between information (or some other valid reason to alter the definition of internal state). Such changes cause a rippling effect in your code because it was built to use the BankAccount class, as originally designed (with CustomerName being a string), and you must now change code that accesses that state throughout your entire application.
The object-oriented principle of Encapsulation helps avoid such problems, allowing you to hide internal state and abstract access to it though type members such as methods, properties, and indexers. Encapsulation helps you reduce coupling between objects and increases the maintainability of your code.

Type Member Access Modifiers

An access modifier allows you to specify the visibility of code outside a type or assembly. Access modifiers can be applied to either types or type members. A later section on Type Access Modifiers discusses modifiers that can be applied to types. This section discusses those modifiers that apply to type members and how they affect visibility.
Generally, you should hide the internal state of your object from direct access from outside code. Then implement other members, such as methods and properties, that wrap that state. This allows the internal implementation of the state to change at will, while the members wrapping the state can still return a representation of the state that doesn't change. This means that outside code will access your object via members that wrap state and be guaranteed that the type of information extracted is consistent. Additionally, because external code doesn't have access to the internal state of your object, they can't alter that state in an inconsistent manner that could break the way your object works.
The first step in encapsulating object state is to determine what type of access that outside code should have to the members of your type. This is performed with access modifiers. The type of access granted varies from no external access at all to full public access and a few variations in between the extremes. Table 19-1 lists all of the type member access modifiers and explains their meaning.
Table 19-1. Type member access modifiers control what code has access to a specified type member.

Access Modifier Description (who can access)
private Only members within the same type. (default for type members)
protected Only derived types or members of the same type.
internal Only code within the same assembly. Can also be code external to object as long as it is in the same assembly. (default for types)
protected internal Either code from derived type or code in the same assembly. Combination of protected OR internal.
public Any code. No inheritance, external type, or external assembly restrictions.
As you've learned from previous lessons of the C# Tutorial, types contain several types of members, including constructors, properties, indexers, methods, and others.  Rather than show you an exhaustive list of all of the permutations of access modifiers you can use with these members, I'll take a more practical approach and describe a sub-set of access modifiers used on properties and methods.

Opening Type Members to public Access

You've seen the public access modifier used in earlier parts of the C# Tutorial. Any time the public access modifier is used on a type member, calling code will be able to access the type member. If you make your type member public, you are giving everyone permission to use it. Listing 19-1 shows an example of using the public access modifier on a method.
Listing 19-1. Declaring a Method with a public Access Modifier: BankAccountPublic.cs
using System;

class BankAccountPublic
{
    public decimal GetAmount()
    {
        return 1000.00m;
    }
}

The GetAmount() method in Listing 19-1 is public meaning that it can be called by code that is external to this class. Now, you can write the following code, elsewhere in your program, to use this method:
 BankAccountPublic bankAcctPub = new BankAccountPublic();

// call a public method
decimal
amount = bankAcctPub.GetAmount();
All you need to do, as shown above, is create an instance of the class that contains the method and then call the method through that instance. Because it is public, you won't have a problem. Remember that the default access for a type member is private, which we'll talk about next. This means that if you forget the public modifier, and didn't use any modifier at all, you would receive a compiler error.

Hiding Type Members with private Access

A private type member is one that can only be accessed by members within the same type. For example, if the BankAccount class has a private member, only other members of the BankAccount class can access or call that member.
Although the default access for type members is private, I prefer to be explicit about my intentions when declaring type members and include the access modifier, rather than rely on defaults. I think it makes the code easier to read and makes it clear to other developers what my true intention is. Listing 19-2 shows how to use the private access modifier and offers an example of why you would want to use it.
Listing 19-2. Declaring a private Field: BankAccountPrivate.cs
using System;

class
BankAccountPrivate
{
   
private string m_name;

   
public string CustomerName
    {
       
get { return m_name; }
       
set { m_name = value; }
    }
}

It's common to encapsulate the state of your type with properties. In fact, I always wrap my type state in a property. In Listing 19-2, you can see how the name of the customer is held in the m_name field, but it is wrapped (encapsulated) with the CustomerName property. Because m_name is declared as private, code outside the BankAccountPrivate class can't access it directly. They must use the public CustomerName property instead.
Now you can change the implementation of m_name in any way you want. For example, what if you wanted it to be an ID of type int and the CustomerName property would do a search to find the name or what if you wanted to have first and last name values that the CustomerName property could concatenate. There are all kinds of things happening to your code in maintenance that will causes implementation to change. The point is that private members allow the implementation to change without constraining the implementation or causing rippling effects throughout your code base that would have occurred if that external code had access to the members of your type.
The private and public access modifiers are at the two extremes of access, either denying all external access or allowing all external access, respectively. The other access modifiers are like different shades of gray between these two extremes, including the protected modifier, discussed next.

Access for Derived Types with the protected Access Modifier

In some ways, the protected access modifier acts like both the private and public access modifiers. Like private, it only allows access to members within the same type, except that it acts like public only to derived types. Said another way, protected type members can only be accessed by either members within the same type or members of derived types.
Returning to the BankAccount example, what if you needed to call code to close an account? Furthermore, what if there were different types of accounts? Each of these different account types would have their own logic for closing, but the basic process would be the same for all account types. If this sounds to you like the description of Polymorphism, you would be on the right track. Back in Lesson 9, we discussed polymorphism and how it allows us to treat different classes the same way. You may want to refer to Lesson 9 for a refresher before looking at the next example.
In the case of closing an account, there are several things that need to be done like calculating interest that is due, applying penalties for early withdrawal, and doing the work to remove the account from the database. Individually, you don't want any code to call methods of the BankAccount class unless all of the methods are called and each method is called in the right order. For example, what if some code called the method to delete the account from the database and didn't calculate interest or apply penalties? Someone would lose money. Also, if the calling code were to delete the account first then the other methods would run into errors because the account information isn't available. Therefore, you need to control this situation and Listing 19-3 shows how you can do it.
Listing 19-3. Declaring protected Methods: BankAccountProtected.cs
using System;

class
BankAccountProtected
{
   
public void CloseAccount()
    {
        ApplyPenalties();
        CalculateFinalInterest();
        DeleteAccountFromDB();
    }

   
protected virtual void ApplyPenalties()
    {
       
// deduct from account
    }

   
protected virtual void CalculateFinalInterest()
    {
       
// add to account
    }

   
protected virtual void DeleteAccountFromDB()
    {
       
// send notification to data entry personnel
    }
}

The most important parts of Listing 19-3 are that the CloseAccount method is public and the other methods are protected. Any calling code can instantiate BankAccountProtected, but it can only call the CloseAccount method. This gives you protection from someone invoking the behavior of your object in inappropriate ways. Your business logic is sound.
At the end of this section, you'll see an example of how to call the code in Listing 19-3. For now, it is essential that you see how the other pieces fit together first.
If you only wanted the BankAccountProtected class to operate on its own members, you could have made the protected methods private instead. However, this code supports a framework where you can have different account types such as Savings, Checking, and more. You will be able to add new account types in the future because the BankAccountProtected class is designed to support them with protected virtual methods. Listings 19-4 and 19-5 show you the SavingsAccount and CheckingAccount classes that derive from the BankAccountProtected class.
Listing 19-4. Derived SavingsAccount Class Using protected Members of its Base Class: SavingsAccount.cs
using System;

class
SavingsAccount : BankAccountProtected
{
   
protected override void ApplyPenalties()
    {
       
Console.WriteLine("Savings Account Applying Penalties");
    }

   
protected override void CalculateFinalInterest()
    {
       
Console.WriteLine("Savings Account Calculating Final Interest");
    }

   
protected override void DeleteAccountFromDB()
    {
        base.DeleteAccountFromDB();

       
Console.WriteLine("Savings Account Deleting Account from DB");
    }
}

Notice how SavingsAccount derives from BankAccountProtected. SavingsAccount can access any of the protected members of the BankAccountProtected class which is its base class. It demonstrates this fact via the call to base.DeleteAccountFromDB in it's DeleteAccountFromDB method. If the inheritance part of Listing 19-4 is a little confusing, you can visit Lesson 8: Class Inheritance for a refresher and better understanding. Each method of SavingsAccount has the protected access modifier also, which simply means that classes derived from SavingsAccount can access those SavingsAccount members with the protected access modifier. The same situation exists with the CheckingAccount class, shown in Listing 19-5.
Listing 19-5. Derived CheckingAccount Class Using protected Members of its Base Class: CheckingAccount.cs
using System;

class
CheckingAccount : BankAccountProtected
{
   
protected override void ApplyPenalties()
    {
       
Console.WriteLine("Checking Account Applying Penalties");
    }

   
protected override void CalculateFinalInterest()
    {
       
Console.WriteLine("Checking Account Calculating Final Interest");
    }

   
protected override void DeleteAccountFromDB()
    {
        base.DeleteAccountFromDB();
       
Console.WriteLine("Checking Account Deleting Account from DB");
    }
}

The CheckingAccount class in Listing 19-5 is implemented similar to SavingsAccount from Listing 19-4. If you were writing this, the difference would be that the methods of each class would have unique implementations. For example, the business rules associated with the final interest calculation would differ, depending on whether the account type was checking or savings.
Notice the call to the base class method in the DeleteAccountFromDB method in CheckingAccount. Just like SavingsAccount, CheckingAccount has access to BankAccountProtected's protected method because it is a derived class. This is a common pattern in polymorphism because derived classes often have a responsibility to call virtual base class methods to ensure critical functionality has the opportunity to execute. You would consult the method documentation to see if this was necessary. Without a protected access modifier, your only option would have been to make the base class method public; which, as explained earlier, is dangerous.
To use the code from Listings 19-3, 19-4, and 19-5, you can implement the following code:
BankAccountProtected[] bankAccts = new BankAccountProtected[2];
bankAccts[0] =
new SavingsAccount();
bankAccts[1] =
new CheckingAccount();

foreach (BankAccountProtected acct in bankAccts)
{
   
// call public method, which invokes protected virtual methods
    acct.CloseAccount();
}

Since both SavingsAccount and CheckingAccount derive from BankAccountProtected, you can assign them to the bankAccts array. They both override the protected virtual methods of BankAccountProtected, so it is the SavingsAccount and CheckingAccount methods that are called when CloseAccount in BankAccountProtected executes. Remember that the only reason the methods of SavingsAccount and CheckingAccount can call their virtual base class methods, as in the case of DeleteAccountFromDB, is because the virtual base class methods are marked with the protected access modifier.

A Quick Word on internal and protected internal Access Modifiers

In practice, most of the code you write will involve the public, private, and protected access modifiers. However, there are two more access modifiers that you can use in more sophisticated scenarios: internal and protected internal.
You would use internal whenever you created a separate class library and you don't want any code outside of the library to access the code with internal access. The protected internal is a combination of the two access modifiers it is named after, which means either protected or internal.

Access Modifiers for Types

So far, the discussion of access modifiers has only applied to the members of types.  However, the rules are different for the types themselves. When talking about types, I'm referring to all of the C# types, including classes, structs, interfaces, delegates, and enums. Nested types, such as a class defined within the scope of a class, are considered type members and fall under the same access rules as other type members.
Types can have only two access modifiers: public or internal. The default, if you don't specify the access modifier, is internal. Looking at all of the classes used in this lesson, you can see that they are internal because they don't have an access modifier. You can explicitly specify internal like this:
internal class InternalInterestCalculator
{
   
// members go here
}

Perhaps the InternalInterestCalculator, shown above, has special business rules that you don't want other code to use. Now, it is in a class library of its own and can only be accessed by other code inside of that same class library (DLL).
Note: To be more specific, internal means that only code in the same assembly can access code marked as internal. However, discussing the definition of an assembly is outside the scope of this lesson, so I am simplifying the terminology.
If you declared a class inside of a class library that you wanted other code to use, you would give it a public access modifier. The following code shows an example of applying the public access modifier to a type:
public class BankAccountExternal
{
   
// members go here
}

Clearly, a bank account is something you would want to access from outside of a class library. Therefore, it only makes sense to give it a public access modifier as shown in the BankAccountExternal class above.
Tip: A common gottcha in Visual Studio occurs when you create a new class in a class library. The default template doesn't include an access modifier. Then, when you try to write code that uses the new class in your program (which references the class library), you get a compiler error saying that the class doesn't exist. Well, you know it exists because you just wrote it and are looking at the page. If you've already seen the clue I've given you so far, you'll key on the fact that the default template left out the access modifier on the type. This makes the class default to internal, which can't be seen outside of the assembly. The fix is to give the class a public modifier, like the BankAccountExternal class above.

Summary

Encapsulation is an object-oriented principle of hiding the internal state and behavior of an object, making your code more maintainable. In C#, you can manage encapsulation with access modifiers. For example, the public access modifier allows access to any code but the private access modifier restricts access to only members of a type. Other access modifiers restrict access in the range somewhere between public and private. While you can use any of the access modifiers on type members, the only two access modifiers you can use on types are the public and internal.
I invite you to return for Lesson 20: Introduction to Generic Collections.

Lesson 18: Overloading Operators

This lesson shows you how to overload C# operators. Our objectives are as follows:
  • Understand what operator overloading is
  • Determine when it is appropriate to overload an operator
  • Learn how to overload an operator
  • Familiarize yourself with rules for operator overloading

About Operator Overloading

In Lesson 2, you learned what operators were available in C#, which included + (plus), - (minus), ^ (exclusive or), and others. Operators are defined for the built-in types, but that's not all. You can add operators to your own types, allowing them to be used much like the operators with the built-in C# types.
To understand the need for operator overloading, imagine that you need to perform matrix math operations in your program. You could instantiate a couple 2-dimensional arrays and do what you need. However, add the requirement for the matrix behavior to be reusable. Because you need to do the same thing in other programs and want to take advantage of the fact that you have already written the code, you will want to create a new type.
So, you create a Matrix type, which could be a class or a struct. Now consider how this Matrix type would be used. You would want to initialize two or more Matrix instances with data and then do a mathematical operation with them, such as add or get a dot product. To accomplish the mathematical operation, you could implement an Add(), DotProduct(), and other methods to get the job done. Using the classes would look something like this:
Matrix result = mat1.Add(mat2);  // instance
or
Matrix result = Matrix.Add(mat1, mat2);  // static
or even worse
Matrix result = mat1.DotProduct(mat2).DotProduct(mat3); // and so on...
The problem with using methods like this is that it is cumbersome, verbose, and unnatural for the problem you are trying to solve. It would be much easier to have a + operator for the add operation and a * operator for the dot product operation. The following shows how the syntax appears using operators:
Matrix result = mat1 + mat2;
or
Matrix result = mat1 * mat2;
or even better
Matrix result = mat1 * mat2 * mat3 * mat4;
This is much more elegant and easier to work with. For a single operation, one could argue that the amount of work to implement one syntax over the other is not that great. However, when chaining multiple mathematical operations, the syntax is much simpler. Additionally, if the primary users of your type are mathematicians and scientists, operators are more intuitive and natural.

When Not to Use Operator Overloading

A lot of the discussion, so far, has emphasized the need to write code and implement types in the simplest and most natural way possible. A very important concept to remember is that although operators are simple, they are not always natural. In the example above it made sense to use operators with the Matrix type. This is similar to the reason why operators make sense with the built-in types such as int and float. However, it is easy to abuse operators and create convoluted implementations that are hard for anyone, including the original author, to understand.
For an example of a bad implementation, consider a Car class that needs an implementation allowing you to park the car in a garage. It would be a mistake to think that the following implementation was smart:
Car mySedan = new Car();
Garage parkingGarage = new Garage();

mySedan = mySedan + parkingGarage; // park car in the garage
This is bad code. If you ever have the temptation to do something like this - don't. No one will truly understand what it means and they will not think it is clever. Furthermore, it hurts the maintainability of the application because it is so hard to understand what the code does. Although the comment is there, it doesn't help much and if it wasn't there, it would be even more difficult to grasp the concept of adding a Car and a Garage.
The idea is this: Use operators where they lend understanding and simplicity to a type. Otherwise, do not use them.

Implementing an Overloaded Operator

The syntax required to implement an overloaded operator is much the same as a static method with a couple exceptions. You must use the operator keyword and specify the operator symbol being overloaded. Here's a skeleton example of how the dot product operator could be implemented:
public static Matrix operator *(Matrix mat1, Matrix mat2)
{
    // dot product implementation
}
Notice that the method is static. Use the keyword operator after specifying the return type, Matrix in this case. Following the operator keyword, the actual operator symbol is specified and then there is a set of parameters to be operated on. See Listing 18-1 for a full example of how to implement and use an overloaded operator.
Listing 18-1. Implementing an Overloaded Operator: Matrix.cs
using System;

class Matrix
{
 public const int DimSize = 3;
 private double[,] m_matrix = new double[DimSize, DimSize];

 // allow callers to initialize
 public double this[int x, int y]
 {
  get { return m_matrix[x, y]; }
  set { m_matrix[x, y] = value; }
 }

 // let user add matrices
 public static Matrix operator +(Matrix mat1, Matrix mat2)
 {
  Matrix newMatrix = new Matrix();

  for (int x=0; x < DimSize; x++)
   for (int y=0; y < DimSize; y++)
    newMatrix[x, y] = mat1[x, y] + mat2[x, y];

  return newMatrix;
 }
}

class MatrixTest
{
 // used in the InitMatrix method.
 public static Random m_rand = new Random();

 // test Matrix
 static void Main()
 {
  Matrix mat1 = new Matrix();
  Matrix mat2 = new Matrix();

  // init matrices with random values
  InitMatrix(mat1);
  InitMatrix(mat2);
  
  // print out matrices
  Console.WriteLine("Matrix 1: ");
  PrintMatrix(mat1);

  Console.WriteLine("Matrix 2: ");
  PrintMatrix(mat2);

  // perform operation and print out
            results
  Matrix mat3 = mat1 + mat2;

  Console.WriteLine();
  Console.WriteLine("Matrix 1 + Matrix
            2 = ");
  PrintMatrix(mat3);

  Console.ReadLine();
 }

 // initialize matrix with random values
 public static void InitMatrix(Matrix mat)
 {
  for (int x=0; x < Matrix.DimSize; x++)
   for (int y=0; y < Matrix.DimSize; y++)
    mat[x, y] = m_rand.NextDouble();
 }

 // print matrix to console
 public static void PrintMatrix(Matrix mat)
 {
  Console.WriteLine();
  for (int x=0; x < Matrix.DimSize; x++)
  {
   Console.Write("[ ");
   for (int y=0; y < Matrix.DimSize; y++)
   {
    // format the output
    Console.Write("{0,8:#.000000}", mat[x, y]);

    if ((y+1 % 2) < 3)
     Console.Write(", ");
   }
   Console.WriteLine(" ]");
  }
  Console.WriteLine();
 }
}
Similar to the skeleton example of the dot product operator, the Matrix class in Listing 18-1 contains an operator overload for the + operator. For your convenience, I've extracted the pertinent overload implementation in the code below:
 // let user add matrices
 public static Matrix operator +(Matrix mat1, Matrix mat2)
 {
  Matrix newMatrix = new Matrix();

  for (int x=0; x < DimSize; x++)
   for (int y=0; y < DimSize; y++)
    newMatrix[x, y] = mat1[x, y] + mat2[x, y];

  return newMatrix;
 }
The operator is static, which is the only way it can and should be declared because an operator belongs to the type and not a particular instance. There are just a few rules you have to follow when implementing operator overloads. What designates this as an operator is the use of the keyword operator, followed by the + symbol. The parameter types are both of the enclosing type, Matrix. The implementation of the operator overload creates a new instance of the return type and performs a matrix add.

Operator Rules

C# enforces certain rules when you overload operators. One rule is that you must implement the operator overload in the type that will use it. This is sensible because it makes the type self-contained.
Another rule is that you must implement matching operators. For example, if you overload ==, you must also implement !=. The same goes for <= and >=.
When you implement an operator, its compound operator works also. For example, since the + operator for the Matrix type was implemented, you can also use the += operator on Matrix types.

Summary

Operator overloading allows you to implement types that behave like the built-in types when using operators. Be sure to use operators in a way that is natural and understandable for the type. Syntax for implementing operators is much like a static method, but includes the operator keyword and the operator symbol in place of an identifier. Additionally, there are rules, such as maintaining symmetry,for using operators, which encourage construction of robust types.
I invite you to return for Lesson 19: Encapsulation.

Lesson 17: Enums


 

This lesson explains how to use C# enums. Our objectives are as follows:
  • Understand what an enum is
  • Be able to create new enum types
  • Learn how to use enums
  • Gain familiarity with System.Enum type methods

Enums Defined

Enums are strongly typed constants. They are essentially unique types that allow you to assign symbolic names to integral values. In the C# tradition, they are strongly typed, meaning that an enum of one type may not be implicitly assigned to an enum of another type even though the underlying value of their members are the same. Along the same lines, integral types and enums are not implicitly interchangable. All assignments between different enum types and integral types require an explicit cast.
Enums lend themselves to more maintainable code because they are symbolic, allowing you to work with integral values, but using a meaningful name to do so. For example, what type of code would you rather work with - a set of values named North, South, East, and West or the set of integers 0, 1, 2, and 3 that mapped to the same values, respectively? Enums make working with strongly typed constants via symbolic names easy.
Enums are value types, which means they contain their own value, can't inherit or be inherited from, and assignment copies the value of one enum to another. You will see in this lesson and elsewhere that enums are used and referred to with both lower case, enum, and upper case, Enum. The relationship between the two is that the C# type, enum, inherits the Base Class Library (BCL) type, Enum. Use the C# type, enum, to define new enums and use the BCL type, Enum, to implement static enum methods.

Creating an Enum

The .NET Framework Class Library contains many enums and examples of how they are used. For example, every time you put an icon on a MessageBox, you use the MessageBoxIcon enum. For a list of available enums in the .NET Framework Class Library, look at the documentation for the Enum class and click on the Derived Classes link.
Whenever there are situations where you are using a set of related numbers in a program, consider replacing those numbers with enums. It will make a program more readable and type safe. Listing 17-1 contains an enum definition and code that uses that enum in a switch statement. Instead of using the numbers 0, 1, and 2 in the switch statement, the code is more meaningful through the use of the Volume enum.
Listing 17-1. Creating and Using an Enum: EnumSwitch.cs
using System;

// declares the enum
public enum Volume
{
   Low,
   Medium,
   High
}

// demonstrates how to use the enum

class
EnumSwitch
{
   static void Main()
   {
      // create and initialize
      // instance of enum type
      Volume myVolume = Volume.Medium;

      // make decision based
      // on enum value
      switch (myVolume)
      {
         case Volume.Low:
            Console.WriteLine("The volume has been turned Down.");
            break;
         case Volume.Medium:
            Console.WriteLine("The volume is in the middle.");
            break;
         case Volume.High:
            Console.WriteLine("The volume has been turned up.");
            break;
      }
      Console.ReadLine();
   }
}

Listing 17-1 contains a definition for an enum. Notice that it is declared with the enum keyword, has a type identifier (Volume), and contains a comma separated list of values enclosed within curly braces.
This enum is of type Volume and we use it to declare the myVolume variable in the Main method. Since an enum is a value type, we can assign a value (Volume.Medium) to it directly, similar to the simple types such as int or double. Once the myVolume variable is declared and initialized, it is used in the switch statement.Each of the case statements represent a unique member of the Volume enum.
Any time a member of the Volume enum is used, it is fully qualified with the "Volume" identifier to guarantee type safety. For example, if there were a Meat enum in scope, then Meat.Medium would definitely have different semantics than Volume.Medium. With both enums in scope, it would be ambiguous to just use the Medium identifier without type qualification. Using the type identifier ensures such mistakes are not made.

Using Enums

An enum is typically specified as shown in Listing 17-1, but may be customized by changing its base type and member values. By default, the underlying type of an enum is int. This default may be changed by specifying a specific base when declaring the enum. You would specify a different base if the enum was used extensively and there was an opportunity for space savings by selecting a smaller type. Another reason may be if you wanted the underlying type of the enum to correspond to another type in your program and you wanted to explicitly cast between the two without loss of precision. Valid base types include byte, sbyte, short, ushort, int, uint, long, and ulong.
Another modification you can make to an enum is to set the value of any enum member. By default, the first member of an enum takes the value of zero. If this value doesn't make sense for your enum, you can change it to one or some other number. Additionally, you can change any of the members of an enum to any value that is valid for its base type. Unassigned enum members have a value that is one more than their predecessor. Listing 17-2 shows how to modify the base type and member values of an enum.
Listing 17-2. Setting the Enum Base and Initializing Members: EnumBaseAndMembers.cs
using System;

// declares the enum
public enum Volume : byte
{
    Low = 1,
    Medium,
    High
}

class EnumBaseAndMembers
{
    static void Main()
    {
        // create and initialize
        // instance of enum type
        Volume myVolume = Volume.Low;

        // make decision based
        // on enum value
        switch (myVolume)
        {
            case Volume.Low:
                Console.WriteLine("The volume has been turned Down.");
                break;
            case Volume.Medium:
                Console.WriteLine("The volume is in the middle.");
                break;
            case Volume.High:
                Console.WriteLine("The volume has been turned up.");
                break;
        }
        Console.ReadLine();
    }
}

The Volume enum in Listing 17-2 shows how to modify the base type and members of an enum.Its base type is changed to byte with the : <type> syntax following the enum identifier, Volume.This ensures that the Volume enum may only have members with values that are valid for type byte.
The first member of the Volume enum, Low, has its value changed to 1. The same syntax, <member> = <value>, may be applied to any member of the enum. You are restricted from creating forward references, circular references, and duplicate references in enum members.
The default values of the Volume enum are Low=0, Medium=1, and High=2 because the first member of an enum defaults to 0 and the following members default to one more than their predecessor. However, the Volume enum in Listing 17-2 has its Low member set to 1, which means that Medium=2 and High=3.

Enum tricks

Enum types implicitly inherit the System.Enum type in the Base Class Library (BCL). This also means that you can use the members of System.Enum to operate on enum types. This section does just that, showing some useful tips and tricks to use with enums in your programs.
A common requirement with enums is to convert between the enum and a variable of its base type. For example, if you are getting input in the form of an int from a user or a file stream, then you can cast it to an enum and use it in a meaningful way in your program. You can also get a complete list of enum member names or enum values, which is useful if you have logic that needs to iterate through every enum member. Listing 17-3 shows how to perform conversions between enums and their base types and how to use some of the System.Enum type members.
Listing 17-3. Enum Conversions and using the System.Enum Type: Enumtricks.cs
using System;

// declares the enum
public enum Volume : byte
{
    Low = 1,
    Medium,
    High
}


// shows different ways
// to work with enums
class Enumtricks
{
    static void Main(string[] args)
    {
        // instantiate type
        Enumtricks enumtricks = new Enumtricks();

        // demonstrates explicit cast
        // of int to Volume
        enumtricks.GetEnumFromUser();

        // iterate through Volume enum by name
        enumtricks.ListEnumMembersByName();

        // iterate through Volume enum by value
        enumtricks.ListEnumMembersByValue();

        Console.ReadLine();
    }

    // demonstrates explicit cast
    // of int to Volume
    public void GetEnumFromUser()
    {
        Console.WriteLine("\n----------------");
        Console.WriteLine("Volume Settings:");
        Console.WriteLine("----------------\n");

        Console.Write(@"
1 - Low
2 - Medium
3 - High

Please select one (1, 2, or 3): ");

        // get value user provided
        string volString = Console.ReadLine();
        int volInt = Int32.Parse(volString);

        // perform explicit cast from
        // int to Volume enum type
        Volume myVolume = (Volume)volInt;

        Console.WriteLine();

        // make decision based
        // on enum value
        switch (myVolume)
        {
            case Volume.Low:
                Console.WriteLine("The volume has been turned Down.");
                break;
            case Volume.Medium:
                Console.WriteLine("The volume is in the middle.");
                break;
            case Volume.High:
                Console.WriteLine("The volume has been turned up.");
                break;
        }

        Console.WriteLine();
    }

    // iterate through Volume enum by name
    public void ListEnumMembersByName()
    {
        Console.WriteLine("\n---------------------------- ");
        Console.WriteLine("Volume Enum Members by Name:");
        Console.WriteLine("----------------------------\n");

        // get a list of member names from Volume enum,
        // figure out the numeric value, and display
        foreach (string volume in Enum.GetNames(typeof(Volume)))
        {
            Console.WriteLine("Volume Member: {0}\n Value: {1}",
                volume, (
byte)Enum.Parse(typeof(Volume), volume));
        }
    }

    // iterate through Volume enum by value
    public void ListEnumMembersByValue()
    {
        Console.WriteLine("\n----------------------------- ");
        Console.WriteLine("Volume Enum Members by Value:");
        Console.WriteLine("-----------------------------\n");

        // get all values (numeric values) from the Volume
        // enum type, figure out member name, and display
        foreach (byte val in Enum.GetValues(typeof(Volume)))
        {
            Console.WriteLine("Volume Value: {0}\n Member: {1}",
                val, Enum.GetName(
typeof(Volume), val));
        }
    }
}

The code in Listing 17-3 includes three method calls to GetEnumFromUser, ListEnumMembersByName, and ListEnumMembersByValue. Each of these methods demonstrate a different aspect of using System.Enum to work with enums.
The GetEnumFromUser method shows how to obtain int input and translate it to an appropriate enum type. Converting an int to an enum makes the code more readable and type safe. The following is an excerpt from Listing 17-3 that shows the pertinent part of the code that performs the conversion:
        // get value user provided
        string volString = Console.ReadLine();
        int volInt = Int32.Parse(volString);

        // perform explicit cast from
        // int to Volume enum type
        Volume myVolume = (Volume)volInt;
After the program displays a menu, it prompts the user for a selection in the form of a number (1, 2, or 3). When the user makes a selection and presses the Enter key, the code reads the value with Console.ReadLine, which returns the value as a string type. Since you can only cast an int to a Volume enum type, the user's input must be converted from a string to an int with the Int32.Parse method. Converting the int to a Volume enum type is simply a matter of applying a cast operation during assignment.
To get all the members of an enum at the same time, you can use the GetNames method of the System.Enum type, which returns a string array of the names of all an enum's members. An excerpt from the ListEnumMembersByName method in Listing 17.3 that shows this appears below:
        // get a list of member names from Volume enum,
        // figure out the numeric value, and display
        foreach (string volume in Enum.GetNames(typeof(Volume)))
        {
            Console.WriteLine("Volume Member: {0}\n Value: {1}",
                volume, (
byte)Enum.Parse(typeof(Volume), volume));
        }

Because GetNames returns an array of strings, it is easy to use in a loop statement such as foreach. Something you may be curious about in the code above is the second parameter to the WriteLine method's format string. Given the enum type and a string representation of the member name, you can use the Enum.Parse method to get the underlying value of that member. Because the Volume enum's base type is byte, the return value from Enum.Parse must be cast to a byte before assignment, forcing the numeric representation of the enum value to appear. If we would have omitted the byte cast, the output would be the Volume enum member, which would then be converted to a string representation of the member name, which is not what the code intended to show.
Instead of getting names of all the members of an enum, you may have a reason to get all the values of the enum at one time. The code below, from the ListEnumMembersByValue method in Listing 17.3, shows how to accomplish this:
        // get all values (numeric values) from the Volume
        // enum type, figure out member name, and display
        foreach (byte val in Enum.GetValues(typeof(Volume)))
        {
            Console.WriteLine("Volume Value: {0}\n Member: {1}",
                val, Enum.GetName(
typeof(Volume), val));
        }

Given the type of the enum, the GetValues method of System.Enum will return an array of the given enum's base type, which in this case is byte. While iterating through this list, each member is printed to the console showing its value and name. The name is obtained by using the GetName method of System.Enum, which accepts an enum type and value for which to get the corresponding name of.

Summary

Enums are lists of strongly typed constants with members that are symbolic names, corresponding to an underlying integral type. Enum base types can be changed and member values can be specified. The System.Enum .NET Framework Class Library type is the base class of enum types and contains methods that allow you to work with enums in different ways, such as working with a list of names or values, converting from value to name, and converting from name to value. For more information on the System.Enum type, see the .NET Framework SDK documentation.
I invite you to return for Lesson 18: Overloading Operators.