Subscribe

RSS Feed (xml)

Using Interface Constraint

The interface constraint enables you to specify an interface that a type argument must implement. The interface constraint serves the same two important purposes as the base class constraint. First, it lets you use the members of the interface within the generic class. Second, it ensures that only type arguments that implement the specified interface are used. This means that for any given interface constraint, the type argument must be either the interface or a class that implements that interface.

The interface constraint uses this form of the where clause:

where T : interface-name

Here, T is the name of the type parameter, and interface-name is the name of the interface. More than one interface can be specified by using a comma-separated list. If a constraint includes both a base class and interface, then the base class must be listed first.

The following program illustrates the interface constraint by reworking the telephone list example shown in the previous section. In this version, the PhoneNumber class has been converted into an interface called IPhoneNumber. This interface is then implemented by Friend and Supplier.

// Use an interface constraint.

using System;

// A custom exception that is thrown if a name or number
// is not found.
class NotFoundException : ApplicationException { }

// An interface that supports a name and phone number.
public interface IPhoneNumber {

  string Number {
    get;
    set;
  }

  string Name {
    get;
    set;
  }
}

// A class of phone numbers for friends.
// It implements IPhoneNumber.
class Friend : IPhoneNumber {
  string name;
  string number;

  bool isWorkNumber;

  public Friend(string n, string num, bool wk) {
    name = n;
    number = num;

    isWorkNumber = wk;
  }

  public bool IsWorkNumber {
    get {
      return isWorkNumber;
    }
  }

  // Implement IPhoneNumber
  public string Number {
    get { return number; }
    set { number = value; }
  }

  public string Name {
    get { return name; }
    set { name = value; }
  }

  // ...
}

// A class of phone numbers for suppliers.
class Supplier : IPhoneNumber {
  string name;
  string number;

  public Supplier(string n, string num) {
    name = n;
    number = num;
  }
  // Implement IPhoneNumber
  public string Number {
    get { return number; }
    set { number = value; }
  }

  public string Name {
    get { return name; }
    set { name = value; }
  }

  // ...
}

// Notice that this class does not implement IPhoneNumber.
class EmailFriend {

  // ...
}

// PhoneList can manage any type of phone list
// as long as it implements IPhoneNumber.
class PhoneList<T> where T : IPhoneNumber {
  T[] phList;
  int end;

  public PhoneList() {
    phList = new T[10];
    end = 0;
  }

  public bool add(T newEntry) {
    if(end == 10) return false;

    phList[end] = newEntry;
    end++;

    return true;
  }

  // Given a name, find and return the phone info.
  public T findByName(string name) {

    for(int i=0; i<end; i++) {

      // Name can be used because it is a member of
      // IPhoneNumber, which is the interface constraint.
      if(phList[i].Name == name)
        return phList[i];

    }

    // Name not in list.
    throw new NotFoundException();
  }
  // Given a number, find and return the phone info.
  public T findByNumber(string number) {

    for(int i=0; i<end; i++) {

      // Number can be used because it is also a member of
      // IPhoneNumber, which is the interface constraint.
      if(phList[i].Number == number)
        return phList[i];
    }

    // Number not in list.
    throw new NotFoundException();
  }

  // ...
}

// Demonstrate interface constraints.
class UseInterfaceConstraint {
  public static void Main() {

    // The following code is OK because Friend
    // implements IPhoneNumber.
    PhoneList<Friend> plist = new PhoneList<Friend>();
    plist.add(new Friend("Tom", "555-1234", true));
    plist.add(new Friend("Gary", "555-6756", true));
    plist.add(new Friend("Matt", "555-9254", false));

    try {
      // Find the number of a friend given a name.
      Friend frnd = plist.findByName("Gary");

      Console.Write(frnd.Name + ": " + frnd.Number);

      if(frnd.IsWorkNumber)
        Console.WriteLine(" (work)");
      else
        Console.WriteLine();
    } catch(NotFoundException) {
      Console.WriteLine("Not Found");
    }

    Console.WriteLine();

    // The following code is also OK because Supplier
    // implements IPhoneNumber.
    PhoneList<Supplier> plist2 = new PhoneList<Supplier>();
    plist2.add(new Supplier("Global Hardware", "555-8834"));
    plist2.add(new Supplier("Computer Warehouse", "555-9256"));
    plist2.add(new Supplier("NetworkCity", "555-2564"));

    try {
      // Find the name of a supplier given a number
      Supplier sp = plist2.findByNumber("555-2564");
      Console.WriteLine(sp.Name + ": " + sp.Number);
    } catch(NotFoundException) {
        Console.WriteLine("Not Found");
    }

    // The following declaration is invalid
    // because EmailFriend does NOT implement IPhoneNumber.
//    PhoneList<EmailFriend> plist3 =
//        new PhoneList<EmailFriend>(); // Error!
  }
}

Using the new( ) Constructor Constraint

The new( ) constructor constraint enables you to instantiate an object of a generic type. Normally, you cannot create an instance of a generic type parameter. However, the new( ) constraint changes this because it requires that a type argument supply a parameterless constructor. (This parameterless constructor can be the default constructor provided automatically when no explicit constructor is declared.) With the new( ) constraint in place, you can invoke the parameterless constructor to create an object.

Here is a simple example that illustrates the use of new( ):

// Demonstrate a new() constructor constraint.

using System;

class MyClass {

  public MyClass() {
    // ...
  }

  //...
}

class Test<T> where T : new() {
  T obj;

  public Test() {
    // This works because of the new() constraint.
    obj = new T(); // create a T object
  }

  // ...
}

class ConsConstraintDemo {
  public static void Main() {

    Test<MyClass> x = new Test<MyClass>();

  }
}

First, notice the declaration of the Test class, shown here:

class Test<T> where T : new() {

Because of the new( ) constraint, any type argument must supply a parameterless constructor. As explained, this can be the default constructor or one that you create.

Next, examine the Test constructor, shown here:

public Test() {
  // This works because of the new() constraint.
  obj = new T(); // create a T object
}

A new object of type T is created, and a reference to it is assigned to obj. This statement is valid only because the new( ) constraint ensures that a constructor will be available. To prove this, try removing the new( ) constraint, and then attempt to recompile the program. As you will see, an error will be reported.

In Main( ), an object of type Test is instantiated, as shown here:

Test<MyClass> x = new Test<MyClass>();

Notice that the type argument is MyClass and that MyClass defines a parameterless constructor. Thus, it is valid for use as a type argument for Test. It must be pointed out that it was not necessary for MyClass to explicitly declare a parameterless constructor. Its default constructor would also satisfy the constraint. However, if a class needs other constructors in addition to a parameterless one, then it would be necessary to also explicitly declare a parameterless version.

Two important points about using new( ): First, it can be used with other constraints, but it must be the last constraint in the list. Second, new( ) allows you to construct an object using only the parameterless constructor, even when other constructors are available. In other words, it is not permissible to pass arguments to the constructor of a type parameter.

Technorati :

No comments:

Post a Comment

Archives

LocalsAdda.com-Variety In Web World

Fun Mail - Fun in the Mail