Overview


Generics are not a completely new construct; similar concepts exist with other languages. For example, C++ templates can be compared to generics. However, there's a big difference between C++ templates and .NET generics. With C++ templates the source code of the template is required when a template is instantiated with a specific type. Contrary to C++ templates, generics are not only a construct of the C# language; generics are defined with the CLR. This makes it possible to instantiate generics with a specific type in Visual Basic even though the generic class was defined with C#.

The following sections look into the advantages and disadvantages of generics.

Performance

One of the big advantages of generics is performance. In Chapter 9, collection classes from the namespace System.Collections were used to work with multiple objects. Adding a value type to such a collection results in boxing and unboxing when the value type is converted to a reference type and vice versa.

Note

Boxing and unboxing is discussed in Chapter 5, "Operators and Casts." Here is just a short refresher about these terms.

Note

Value types are stored on the stack. Reference types are stored on the heap. C# classes are reference type; structs are value types. .NET makes it easy to convert value types to reference types, and so you can use a value type everywhere an object (which is a reference type) is needed. For example, an int can be assigned to an object. The conversion from a value type to a reference type is known as boxing. Boxing happens automatically if a method requires an object as a parameter, and a value type is passed. On the other side, a boxed value type can be converted to a value type using unboxing. With unboxing the cast operator is required.

The following example shows the ArrayList class from the namespace System.Collections. ArrayList stores objects, the Add() method is defined to require an object as a parameter, and so an integer type is boxed. When the values from an ArrayList are read, unboxing occurs when the object is converted to an integer type. This may be obvious with the cast operator that is used to assign the first element of the ArrayList collection to the variable i1, but also happens inside the foreach statement where the variable i2 of type int is accessed:

 ArrayList list = new ArrayList(); list.Add(44);   // boxing – convert a value type to a reference type int i1 = (int)list[0];   // unboxing – convert a reference type to a value type foreach (int i2 in list) { Console.WriteLine(i2);   // unboxing } 

Boxing and unboxing is easy to use, but it has a big performance impact, especially when iterating through many items.

Instead of using objects, the List<T> class from the namespace System.Collections.Generic allows you to define the type when it is used. In the example here, the generic type of the List<T> class is defined as int, and so the int type is used inside the class that is generated dynamically from the JIT compiler. Boxing and unboxing no longer happens:

 List<int> list = new List<int>(); list.Add(44);   // no boxing – value types are stored in the List<int> int i1 = list[0];   // no unboxing, no cast needed foreach (int i2 in list) { Console.WriteLine(i2); } 

Type Safety

Another feature of generics is type safety. As with the ArrayList class, if objects are used, any type can be added to this collection. This example shows adding an integer, a string, and an object of type MyClass to the collection of type ArrayList:

 ArrayList list = new ArrayList(); list.Add(44); list.Add("mystring"); list.Add(new MyClass()); 

Now if this collection is iterated using the following foreach statement which iterates using integer elements, the compiler accepts this code. However, because not all elements in the collection can be cast to an int, a runtime exception will occur:

 foreach (int i in list) { Console.WriteLine(i); } 

Errors should be detected as early as possible. With the generic class List<T>, the generic type T defines what types are allowed. With a definition of List<int>, only integer types can be added to the collection. The compiler doesn't compile this code because the Add() method has invalid arguments:

 List<int> list = new List<int>(); list.Add(44); list.Add("mystring");   // compile time error list.Add(new MyClass());   // compile time error 

Binary Code Reuse

Generics allow better binary code reuse. A generic class can be defined once and can be instantiated with many different types. You needn't access the source code as is necessary with C++ templates.

As an example, here the List<T> class from the namespace System.Collections.Generic in the assembly mscorlib is instantiated with an int, a string, and a MyClass type:

 List<int> list = new List<int>(); list.Add(44); List<string> stringList = new List<string>(); stringList.Add("mystring"); List<MyClass> myclassList = new List<MyClass>(); myClassList.Add(new MyClass()); 

Generic types can be defined in one language and used from any other .NET language.

Code Bloat

How much code is created with generics when instantiating them with different specific types?

Because a generic class definition goes into the assembly, instantiating generic classes with specific types doesn't duplicate these classes in the IL code. However, when the generic classes are compiled by the JIT compiler to native code, a new class for every specific value type is created. Reference types share all the same implementation of the same native class. This is because with reference types only a 4-byte memory address (with 32-bit systems) is needed within the generic instantiated class to reference a reference type. Value types are contained within the memory of the generic instantiated class, and because every value type can have different memory requirements, a new class for every value type is instantiated.

Naming Guidelines

If generics are used in the program, it helps when generic types can be distinguished from non-generic types. Here are naming guidelines for generic types:

  • Generic type names should be prefixed with the letter T.

  • If the generic type can be replaced by any class because there's no special requirement, and only one generic type is used, the character T is good as a generic type name:

     public class List<T> { } public class LinkedList<T> { } 
  • If there's a special requirement for a generic type (for example, it must implement an interface or derive from a base class), or if two or more generic types are used, descriptive names should be used for the type names:

     public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e); public delegate TOutput Converter<TInput, TOutput>(TInput from); public class SortedList<TKey, TValue> { } 




Professional C# 2005
Pro Visual C++ 2005 for C# Developers
ISBN: 1590596080
EAN: 2147483647
Year: 2005
Pages: 351
Authors: Dean C. Wills

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net