Subscribe

RSS Feed (xml)

Generic Interfaces

The data type upon which it operates is now specified by a type parameter.
// Demonstrate a generic interface.

using System;

public interface ISeries {
  T getNext(); // return next element in series
  void reset(); // restart the series
  void setStart(T v); // set the starting element
}

// Implement ISeries.
class ByTwos : ISeries {
  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 intBT = new ByTwos(intPlusTwo);

    for(int i=0; i < 5; i++)
      Console.Write(intBT.getNext() + "  ");
    Console.WriteLine();


    // Demonstrate double series.
    ByTwos dblBT = new ByTwos(doublePlusTwo);

    dblBT.setStart(11.4);

    for(int i=0; i < 5; i++)
      Console.Write(dblBT.getNext() + "  ");

    Console.WriteLine();


    // Demonstrate ThreeD series.
    ByTwos ThrDBT = new ByTwos(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 {
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 : ISeries {
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 { // 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 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 : ISeries 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 would be valid. ByTwos and ByTwos would be invalid.

No comments:

Post a Comment

Archives

LocalsAdda.com-Variety In Web World

Fun Mail - Fun in the Mail