Building Generic Types

A generic type is any C# type that accepts a type parameter, as described in the preceding section. The hardest part of building generic types isn't the technical details behind the syntax implementation; it is in the design of the types themselves. Although generics are an incredibly powerful tool, the use of generics purely for the sake of using generics can cause its own unique set of problems. The next section of this chapter will cover the basics of working with generic classes, Interfaces, and methods.

Creating Generic Classes

You have seen several examples of how to create and consume generic classes. Generic classes are designed to encapsulate operations and provide models that are not specific to a single data type. The operations encapsulated by generic classes can either apply to all data types, or specific data types such as all derivatives of a certain base class or all classes that implement a specific interface. To recap the preceding section, you use the type parameter operator (<>) to indicate the use of a generic type parameter within your class definition. In places where there used to be a concrete type reference such as int, string, or object, you can then use the type parameter to allow your generic class to defer the specification of the actual type until the class is instantiated.

Creating Generic Methods

In addition to supplying type parameters for the class itself, you can also supply type parameters for individual methods that belong to a class. A generic method is just a method that takes a type parameter. The important thing to keep in mind is that the type parameter that is accepted by a method is unrelated to any type parameter accepted by the class in which the method is defined. This generic method can make use of the class type parameter, but it isn't necessary.

Listing 6.1 provides an illustration of both writing and consuming generic methods.

Listing 6.1. Creating and Consuming Generic Methods

using System; using System.Collections.Generic; using System.Text; namespace Generics1 {     public class MethodTest     {         public static bool IsGreater<TOperand>(TOperand op1,             TOperand op2) where TOperand : IComparable         {             return (op1.CompareTo(op2) > 0);         }     }     public class MethodTest2<TItem> where TItem : IComparable     {         public bool IsGreater(TItem op1, TItem op2)         {             return MethodTest.IsGreater<TItem>(op1, op2);         }     } } 

The first class, MethodTest, defines a static generic method that takes a single type argument, TOperand, that must implement the IComparable interface. The IsGreater method returns whether op1 is greater than op2, where both op1 and op2 will be of the type indicated by TOperand when the generic method is closed (closing is the process by which an open generic definition is bound to a concrete type during the instantiation or method invocation process).

Next, the generic class MethodTest2 takes a single Parameter Type TItem that also must implement the IComparable interface. This class has a single instance method that invokes the static method IsGreater on the MethodTest class. The preceding code sample not only illustrates how to create and consume generic methods, but it also shows you how the class-level type parameters are completely unrelated to the method-level type parameters, though you can use a class-level type parameter within a generic method if you choose. The following few lines of code show how to use both the MethodTest and the MethodTest2 classes:

Console.WriteLine(     string.Format("Is 2 greater than 3? : {0}",     MethodTest.IsGreater<int>(2, 3))); MethodTest2<string> m2 = new MethodTest2<string>(); Console.WriteLine(     string.Format("Is Z greater than A? : {0}",     m2.IsGreater("Z", "A"))); 

One distinct advantage of generic methods is that the compiler can create shortcuts for you. For example, if you pass two integer arguments to the IsGreater method, you don't need to explicitly tell the compiler that the method is being invoked with integers, as shown in the following code:

Console.WriteLine(                 string.Format("Implicit Type Parameter test, 2 > 3 : {0}",                 MethodTest.IsGreater(2, 3))); 

Creating Generic Interfaces

A generic interface functions in much the same way as a generic class. The interface itself accepts one or more type parameters in the same fashion as the generic class. Instead of utilizing the type parameter in the class definition, the type parameter is then utilized in the member declaration statements that make up the interface.

A huge benefit of allowing generic type parameters to be present in interfaces is that the classes that implement those Interfaces will not need to perform any unnecessary boxing/unboxing or typecasting. You will learn about boxing and unboxing in Chapter 16, "Optimizing your .NET 2.0 Code." For now, it should suffice to know that boxing and unboxing are operations done to switch between value and reference types, and the operation is costly. The code in Listing 6.2 illustrates a generic interface.

Listing 6.2. A Generic Interface

using System; using System.Collections.Generic; using System.Text; namespace Generics1 {   public interface IDataHandler<TRowType> where TRowType : new()   {       void AddRecord(TRowType record);       void DeleteRecord(TRowType record);       void UpdateRecord(TRowType record);       void SelectRecord(TRowType record);       List<TRowType> SelectRecords();   } } 

What the preceding interface accomplishes is indicating that any class that implements IDataHandler<TRowType> will provide the standard Add, Delete, Update, and Select methods typically associated with a data access object regardless of the underlying type of the data being stored. This is an extremely useful interface to have as it allows a lot of flexibility when working with the data tier.

Microsoft Visual C# 2005 Unleashed
Microsoft Visual C# 2005 Unleashed
ISBN: 0672327767
EAN: 2147483647
Year: 2004
Pages: 298 © 2008-2017.
If you may any questions please contact us: