Figure 26.3 reimplements the application of
Fig. 26.1 using a generic
PrintArray method (lines 24-30). Note that the
PrintArray method calls in lines 16, 18 and 20 are identical to those of
Fig. 26.1, the outputs of the two applications are identical and the code in
Fig. 26.3 is 17 lines shorter than the code in
Fig. 26.1. As illustrated in
Fig. 26.3, generics enable us to create and test our code once, then reuse that code for many different types of data. This demonstrates the expressive power of generics.
Line 24 begins method
PrintArray's declaration. All generic method declarations have a
type parameter list delimited by angle brackets
(< E > in this example
) that follows the method's name.
Fig. 26.3
Printing array elements using generic method PrintArray.
|
|
|
|
|
|
4 using System.Collections.Generic;
|
|
|
|
|
|
|
8 static void Main( string[] args )
|
|
|
|
|
11 int[] intArray = { 1, 2, 3, 4, 5, 6 };
|
12 double[] doubleArray = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7 };
|
13 char[] charArray = { 'H', 'E', 'L', 'L', 'O' };
|
|
|
15 Console.WriteLine( "Array intArray contains:" );
|
16 PrintArray( intArray );
|
17 Console.WriteLine( "Array doubleArray contains:" );
|
18 PrintArray( doubleArray );
|
19 Console.WriteLine( "Array charArray contains:" );
|
20 PrintArray( charArray );
|
|
|
|
|
|
|
24 static void PrintArray< E >( E[] inputArray )
|
|
|
26 foreach ( E element in inputArray )
|
27 Console.Write( element + " " );
|
|
|
29 Console.WriteLine( "\n" );
|
|
|
|
|
Array intArray contains:
1 2 3 4 5 6
Array doubleArray contains:
1.1 2.2 3.3 4.4 5.5 6.6 7.7
Array charArray contains: H E L L O
|
|
Each type parameter list contains one or more type parameters, separated by commas. A type parameter is an identifier that is used in place of actual type names. The type parameters can be used to declare the return type, the parameter types and the local variable types in a generic method declaration; the type parameters act as placeholders for the types of the arguments passed to the generic method. A generic method's body is declared like that of any other method. Note that the type parameter names throughout the method declaration must match those declared in the type parameter list. For example, line 26 declares element in the foreach statement as type E, which matches the type parameter (E) declared in line 24. Also, a type parameter can be declared only once in the type parameter list but can appear more than once in the method's parameter list. Type parameter names need not be unique among different generic methods.
 |
|
| If you forget to include the type parameter list when declaring a generic method, the compiler will not recognize the type parameter names when they are encountered in the method. This results in compilation errors.
|
|
Method
PrintArray's type parameter list (line 24) declares type parameter
E as the placeholder for the array element type that
PrintArray will output. Note that
E appears in the parameter list as the array element type (line 24). The
foreach statement header (line 26) also uses
E as the
element type. These are the same two locations where the overloaded
PrintArray methods of Fig. 26.1 specified
int,
double or
char as the array element type. The remainder of
PrintArray is identical to the version presented in Fig. 26.1.
 |
|
| It is recommended that type parameters be specified as individual capital letters. Typically, a type parameter that represents the type of an element in an array (or other collection) is named E for "element" or T for "type." |
|
As in Fig. 26.1, the program of Fig. 26.3 begins by declaring and initializing six-element
int array
intArray (line 11), seven-element
double array
doubleArray (line 12) and five-element
char array
charArray (line 13). Then each array is output by calling
PrintArray (lines 16, 18 and 20)-once with argument
intArray, once with argument
doubleArray and once with argument
charArray.
When the compiler encounters a method call such as line 16, it analyzes the set of methods (both non-generic and generic) that might match the method call looking for a method that matches the call exactly. If there are no exact matches, the compiler picks the best match. If there are no matching methods, or if there is more than one best match, the compiler generates an error. The complete details of method call resolution can be found in Section 14.5.5.1 of the Ecma C# Language Specification
or Section 20.9.7 of the Microsoft C# Language Specification 2.0
In the case of line 16, the compiler determines that an exact match occurs if the type parameter
E in lines 24 and 26 of method
PrintArray's declaration is replaced with the type of the elements in the method call's argument
intArray (i.e.,
int). Then, the compiler sets up a call to
PrintArray with the
int as the
type argument for the type parameter
E. This is known as the
type inferencing process. The same process is repeated for the calls to method
PrintArray in lines 18 and 20.
 |
|
| If the compiler cannot find a single non-generic or generic method declaration that is a best match for a method call, or if there are multiple best matches, a compilation error occurs. |
|
You can also use
explicit type arguments to indicate the exact type that should be used to call a generic function. For example, line 16 could be written as
PrintArray< int >( intArray );
The preceding method call explicitly provides the type argument (
int) that should be used to replace type parameter
E in lines 24 and 26 of the
PrintArray method's declaration.
The compiler also determines whether the operations performed on the method's type parameters can be applied to elements of the type stored in the array argument. The only operation performed on the array elements in this example is to output the
string representation of the elements. Line 27 performs an implicit conversion for every value type array element and an implicit
ToString call on every reference type array element. Since all objects have a
ToString method, the compiler is satisfied that line 27 performs a valid operation for any array element.
By declaring
PrintArray as a generic method in Fig. 26.3, we eliminated the need for the overloaded methods of Fig. 26.1, saving 17 lines of code and creating a reusable method that can output the string representations of the elements in
any array, not just arrays of
int,
double or
char elements.