O'Reilly logo

Visual C# 2005: A Developer's Notebook by Jesse Liberty

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 1. C# 2.0

In this chapter, you will learn about and use the new features in C# 2.0, including generics, iterators, anonymous methods, partial types, static classes, nullable types, and limiting access to properties, as well as delegate covariance and contravariance.

Probably the most exciting and most anticipated new feature in C# 2.0 is generics, which provide you with quick and easy type-safe collections. So, let's start there.

Create a Type-Safe List Using a Generic Collection

Type safety is the key to creating code that's easy to maintain. A type-safe language (and framework) finds bugs at compile time (reliably) rather than at runtime (usually after you've shipped the product!). The key weakness in C# 1.x was the absence of generics , which enable you to declare a general collection (for example, a stack or a list) that can accept members of any type yet will be type-safe at compile time.

In Version 1.x of the framework, nearly all the collections were declared to hold instances of System.Object, and because everything derives from System.Object, these collections could hold any type at all; that is, they were not type-safe.

Suppose, for example, you were creating a list of Employee objects in C# 1.x. To do so, you would use an ArrayList , which holds objects of the System.Object type. Adding new Employees to the collection was not a problem because Employees were derived from System.Object, but when you tried to retrieve an Employee from the ArrayList, all you would get back was an Object reference, which you would then have to cast:

Employee theEmployee = (Employee) myArrayList[1];

An even bigger problem, however, was that there was nothing to stop you from adding a string or some other type to the ArrayList. As long as you never needed to access the string, you would never note the errant type. Suppose, however, that you passed that ArrayList to a method that expected an ArrayList of Employee objects. When that method attempted to cast the String object to the Employee type at runtime, an exception would be thrown.

A final problem with .NET 1.x collections arose when you added value types to the collection. Value types had to be boxed on their way into the collection and explicitly unboxed on their way out.

.NET 2.0 eliminates all these problems with a new library of collections, which you will find in the System.Collections.Generic namespace. A generic collection is simply a collection that allows you to specify its member types when you declare it. Once declared, the compiler will allow only objects of that type to be added to your list. You define generic collections using special syntax; the syntax uses angle brackets to indicate variables that must be defined when an instance of the collection is declared.

There is no need to cast when you retrieve objects from a generic collection, and your code is safer, easier to maintain, and simpler to use than it is with untyped collections such as ArrayList.

Note

With generic collections your code is type-safe, easier to maintain, and simpler to use.

How do I do that?

To get a feel for the new generic types in .NET 2.0, let's use the type-safe List class to create a list of employees. To execute this lab, open Visual Studio 2005, create a new C# Console application, and name it CreateATypeSafeList. Replace the code Visual Studio 2005 creates for you with the code in Example 1-1.

Tip

You must use the System.Collections.Generic namespace to use the generic types. By default Visual Studio 2005 adds this namespace to all projects.

Example 1-1. Creating a type-safe list

using System;
using System.Collections.Generic;
   
namespace CreateATypeSafeList
{
   // a class to store in the List
   public class Employee
   {
      private int empID;
   
      // constructor
      public Employee(int empID)
      {
         this.empID = empID;
      }
   
      // override the ToString method to 
      // display this employee's id
      public override string ToString( )
      {
         return empID.ToString( );
      }
   }      // end class
   
   // Test driver class
   public class Program
   {
      // entry point
      static void Main( )
      {
         // Declare the type safe list (of Employee objects)
         List<Employee> empList = new List<Employee>( );
   
         // Declare a second type safe list (of integers)
         List<int> intList = new List<int>( );
   
         // populate the Lists
         for (int i = 0; i < 5; i++)
         {
            empList.Add(new Employee(i + 100));
            intList.Add(i * 5);
            // empList.Add(i * 5);  // see "What About" section below
         }
   
         // print the integer list
         foreach (int i in intList)
         {
            Console.Write("{0} ", i.ToString( ));
         }
   
         Console.WriteLine("\n");
   
         // print the Employee List
         foreach (Employee employee in empList)
         {
            Console.Write("{0} ", employee.ToString( ));
         }
   
         Console.WriteLine("\n");
      }
   }
}

Output:

0 5 10 15 20 
100 101 102 103 104

Tip

All the source code for the labs in this chapter is available on my web site, http://www.LibertyAssociates.com. Click Books, and then scroll down to C# 2.0 Programmer's Notebook and click Source to save the source code to your computer.

Once unzipped, the source code is in chapter folders, and each lab folder is named with the namespace shown in the listing. For instance, for Example 1-1, the source is stored in Chapter 1\ CreateATypeSafeList.

While you are at my site, you can also read the FAQ list and errata sheet and join a private support discussion forum.

What just happened?

This listing creates two classes: an Employee class to be held in the collection and the Program class created by Visual Studio 2005. It also uses the List class provided by the .NET Framework Class Library.

The Employee class contains a single private field (empID), a constructor, and an override of ToString to return the empID field as a string.

First you create an instance of List that will hold Employee objects. The type of empList is "List of Employee Objects" and is declared thus:

List<Employee> empList

When you see the definition List<T>, the T is a placeholder for the actual type you'll place in that list.

As always, empList is just a reference to the object you create on the heap using the new keyword. The new keyword expects you to invoke a constructor, which you do as follows:

new List<Employee>( )

This creates an instance of "List of Employee Objects" on the heap, and the entire statement, put together, assigns a reference to that new object to empList:

List<Employee> empList = new List<Employee>( );

Tip

This is just like writing:

Dog milo = new Dog( );

in which you create an instance of Dog on the heap and assign it to the reference to Dog, milo.

In the next statement, you create a second List, this time of type "List of Integers":

               List<int> intList = new List<int>( );

Now you are free to add integers to the list of integers, and Employee objects to the list of Employee objects. Once the lists are populated, you can iterate through each of them, using a foreach loop to display their contents in the console window:

foreach (Employee employee in empList)
{
   Console.Write("{0} ", employee.ToString( ));
}

What about...

...if you try to add an integer to the list of Employees?

Try it. Start by uncommenting the following line in Example 1-1 and recompiling the program:

empList.Add(i * 5);

You'll get a pair of compile errors:

   Error   1      The best overloaded method match for 'System.Collections.
Generic.List<ListCollection.Employee>.Add(ListCollection.Employee)' has 
some invalid arguments
   Error   2      Argument '1': cannot convert from 'int' to 'ListCollection.Employee'

The information provided in these two compile errors enable you to determine that it is not legal to add an int to a collection of Employee objects because no implicit conversion or subtype relationship exists from one to the other.

The good news is that this is a compile error, rather than a runtime error, which can sneak out the door and happen only when your client runs the application!

Note

You can store derived types in a type-safe collection. Thus, a collection of Employees will hold a Manager object, if Manager derives from Employee.

...what about other generic collections; are any available?

Other generic collections are available as well. For instance, the Stack and Queue collections, as well as the ICollection interface, are available in type-safe versions in .NET 2.0.

You use these just as you would List<T>. For example, to make a stack of Employee objects, you replace T in the Stack definition (Stack<T>) with the Employee type:

Stack<Employee> employeeStack = new Stack<Employee>( );

Where can I learn more?

You can learn about all the .NET 2.0 generic classes in the MSDN topic titled "Commonly Used Collection Types," and you can read an article on the subject on O'Reilly's ONDotnet.com site at http://www.ondotnet.com/pub/a/dotnet/2004/05/17/liberty.html.

Tip

A document on my web site lists the links I mention in each lab so that you can copy and paste them into your browser. To get it, go to http://www.LibertyAssociates.com, click Books, scroll down to Visual C# 2005: A Developer's Notebook, and click Links.doc.

The next lab will show you how to create your own type-safe collections to supplement those provided by the Framework.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required