Home > Articles > Programming > C#

  • Print
  • + Share This
This chapter is from the book

This chapter is from the book

Generic Methods

You already learned that it is relatively simple to add a generic method to a class when the class is a generic. You did this in the generic class examples so far, and it also works for static methods. Furthermore, you can use generic classes within a generic class, as you did in earlier BinaryTree listings using the following line of code:

public Pair< BinaryTree<T> > SubItems;

Generic methods are methods that use generics even when the containing class is not a generic class or the method contains type parameters not included in the generic class type parameter list. To define generic methods, you add the type parameter syntax immediately following the method name, as shown in the MathEx.Max<T> and MathEx.Min<T> examples in Listing 11.33.

Example 11.33. Defining Generic Methods


   public static class MathEx
{
  public 
   static T Max<T>(T first, params T[] values)
      where T : IComparable
  {
      T maximum = first;
      foreach (T item in values)
      {
          if (item.CompareTo(maximum) > 0)
          {
              maximum = item;
              }
      }
      return maximum;
  }
  public 
   static T Min<T>(T first, params T[] values)
      where T : IComparable
  {
      T minimum = first;

        foreach (T item in values)
        {
            if (item.CompareTo(minimum) < 0)
            {
               minimum = item;
            }
        }
      return minimum;
   }
}

You use the same syntax on a generic class when the method requires an additional type parameter not included in the class type parameter list. In this example, the method is static but C# does not require this.

Note that generic methods, like classes, can include more than one type parameter. The arity (the number of type parameters) is an additional distinguishing characteristic of a method signature.

Type Inferencing

The code used to call the Min<T> and Max<T> methods looks like that shown in Listing 11.34.

Example 11.34. Specifying the Type Parameter Explicitly

Console.WriteLine(
    MathEx.Max<int>(7, 490));
Console.WriteLine(
    MathEx.Min<string>("R.O.U.S.", "Fireswamp"));

The output to Listing 11.34 appears in Output 11.4.

Example 11.4.

490
Fireswamp

Not surprisingly, the type parameters, int and string, correspond to the actual types used in the generic method calls. However, specifying the type is redundant because the compiler can infer the type from the parameters passed to the method. To avoid redundancy, you can exclude the type parameters from the call. This is known as type inferencing, and an example appears in Listing 11.35. The output of this listing appears in Output 11.5.

Example 11.35. Inferring the Type Parameter

Console.WriteLine(
    MathEx.Max(7, 490));
Console.WriteLine(
    MathEx.Min("R.O.U.S'", "Fireswamp"));

Example 11.5.

490
Fireswamp

For type inferencing to be successful, the types must match the method signature. Calling the Max<T> method using MathEx.Max(7.0, 490), for example, causes a compile error. You can resolve the error by either casting explicitly or including the type argument. Also note that you cannot perform type inferencing purely on the return type. Parameters are required for type inferencing to be allowed.

Specifying Constraints

The generic method also allows constraints to be specified. For example, you can restrict a type parameter to implement IComparable. The constraint is specified immediately following the method header, prior to the curly braces of the method block, as shown in Listing 11.36.

Example 11.36. Specifying Constraints on Generic Methods


   public 
   class ConsoleTreeControl
{
    // Generic method Show<T>
    
   public 
   static 
   void Show<T>(BinaryTree<T> tree, int indent)
         
      where T : IComparable                              
    {
        Console.WriteLine("\n{0}{1}",
            "+ --".PadLeft(5*indent, ' '),
            tree.Item.ToString());
        if (tree.SubItems.First != null)
            Show(tree.SubItems.First, indent+1);
        if (tree.SubItems.Second != null)
            Show(tree.SubItems.Second, indent+1);
      }
}

Notice that the Show<T> implementation itself does not use the IComparable interface. Recall, however, that the BinaryTree<T> class did require this (see Listing 11.37).

Example 11.37. BinaryTree<T> Requiring IComparable Type Parameters


   public 
   class BinaryTree<T>
        
      where T: System.IComparable                         
{
    ...
}

Because the BinaryTree<T> class requires this constraint on T, and because Show<T> uses BinaryTree<T>, Show<T> also needs to supply the constraint.

  • + Share This
  • 🔖 Save To Your Account