Generic Interfaces


In addition to having generic classes and methods, you can also have generic interfaces. Generic interfaces are specified just like generic classes. Here is an example that reworks the ISeries interface developed in Chapter 12. (Recall that ISeries defines the interface to a class that generates a series of numbers.) The data type upon which it operates is now specified by a type parameter.

 // Demonstrate a generic interface. using System; public interface ISeries<T> {   T getNext(); // return next element in series   void reset(); // restart the series   void setStart(T v); // set the starting element } // Implement ISeries. class ByTwos<T> : ISeries<T> {   T start;   T val;   // This delegate defines the form of a method   // that will be called when the next element in   // the series is needed.   public delegate T IncByTwo(T v);   // This delegate reference will be assigned the   // method passed to the ByTwos constructor.   IncByTwo incr;   public ByTwos(IncByTwo incrMeth) {     start = default(T);     val = default(T);     incr = incrMeth;   }   public T getNext() {     val = incr(val);     return val;   }   public void reset() {     val = start;   }   public void setStart(T v) {     start = v;     val = start;   } } class ThreeD {   public int x, y, z;   public ThreeD(int a, int b, int c) {     x = a;     y = b;     z = c;   } } class GenIntfDemo {   // Define plus two for int.   static int intPlusTwo(int v) {     return v + 2;   }   // Define plus two for double.   static double doublePlusTwo(double v) {     return v + 2.0;   }   // Define plus two for ThreeD.   static ThreeD ThreeDPlusTwo(ThreeD v) {     if(v==null) return new ThreeD(0, 0, 0);     else return new ThreeD(v.x + 2, v.y + 2, v.z + 2);   }   public static void Main() {     // Demonstrate int series.     ByTwos<int> intBT = new ByTwos<int>(intPlusTwo);     for(int i=0; i < 5; i++)       Console.Write(intBT.getNext() + "  ");     Console.WriteLine();     // Demonstrate double series.     ByTwos<double> dblBT = new ByTwos<double>(doublePlusTwo);     dblBT.setStart(11.4);     for(int i=0; i < 5; i++)       Console.Write(dblBT.getNext() + "  ");     Console.WriteLine();     // Demonstrate ThreeD series.     ByTwos<ThreeD> ThrDBT = new ByTwos<ThreeD>(ThreeDPlusTwo);     ThreeD coord;     for(int i=0; i < 5; i++) {       coord = ThrDBT.getNext();       Console.Write(coord.x + "," +                     coord.y + "," +                     coord.z + "  ");     }     Console.WriteLine();   } }

The output is shown here:

 2  4  6  8  10 13.4  15.4  17.4  19.4  21.4 0,0,0  2,2,2  4,4,4  6,6,6  8,8,8

There are several things of interest in the preceding example. First, notice how ISeries is declared:

 public interface ISeries<T> {

As mentioned, a generic interface uses a syntax similar to that of a generic class.

Now, notice how ByTwos, which implements ISeries, is declared:

 class ByTwos<T> : ISeries<T> {

The type parameter T is specified by ByTwos and is also specified in ISeries. This is important. A class that implements a generic interface must, itself, be generic. For example, the following declaration would be illegal:

 class ByTwos : ISeries<T> { // Wrong!

The type argument required by a generic interface must be passed to the implementing class. Otherwise, there is no way for the interface to receive the type argument.

Next, the current value of the series, val, and the starting value, start, are declared to be objects of the generic type T. Then, a delegate called IncByTwo is declared. This delegate defines the form of a method that will be used to increase an object of type T by two. In order for ByTwos to work with any type of data, there must be some way to define what an increase by two means for each type of data. This is achieved by passing to the ByTwos constructor a reference to a method that performs an increase by two. This reference is stored in incr. When the next element in the series is needed, that method is called through the incr delegate to obtain the next value in the series.

Notice the class ThreeD. It encapsulates three-dimensional (X, Y, Z) coordinates. It is used to demonstrate ByTwos on a class type.

In GenIntfDemo, three increment methods are declared: one for int, one for double, and one for objects of type ThreeD. These are passed to the ByTwos constructor when objects of their respective types are created. Pay special attention to ThreeDPlusTwo( ), shown here:

 // Define plus two for ThreeD. static ThreeD ThreeDPlusTwo(ThreeD v) {   if(v==null) return new ThreeD(0, 0, 0);   else return new ThreeD(v.x + 2, v.y + 2, v.z + 2); }

Notice that it first checks if v is null. If it is, then it returns a new ThreeD object in which all fields are set to zero. The reason for this is that v is set to default(T) by the ByTwos constructor. This value is zero for value types and null for object types. Thus, (unless setStart( ) has been called) for the first increment, v will contain null instead of a reference to an object. This means that for the first increment, a new object is required.

A type parameter for a generic interface can have constraints in the same way that it can for a generic class. For example, this version of ISeries restricts its use to reference types:

 public interface ISeries<T> where T : class {

When this version of ISeries is implemented, the implementing class must also specify the same constraint for T, as shown here:

 class ByTwos<T> : ISeries<T> where T : class {

Because of the reference constraint, this version of ISeries cannot be used on value types. Thus, in the preceding program, only ByTwos<ThreeD> would be valid. ByTwos<int> and ByTwos<double> would be invalid.




C# 2.0(c) The Complete Reference
C# 2.0: The Complete Reference (Complete Reference Series)
ISBN: 0072262095
EAN: 2147483647
Year: 2006
Pages: 300

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