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.

Generic Delegates

Like methods, delegates can also be generic. The syntax is similar to that of a generic method, with the type parameter being specified after the delegate’s name. To declare a generic delegate, use this general form:
delegate ret-type delegate-name<type-parameter-list>(arg-list);
Notice the placement of the type parameter list. It immediately follows the delegate’s name. The advantage of generic delegates is that they let you define, in a type-safe manner, a generalized form that can then be matched to any specific type of method.
The following program demonstrates a generic delegate called SomeOp that has one type parameter called T. It returns type T and takes an argument of type T.
// A simple generic delegate.

using System;

// Declare a generic delegate.
delegate T SomeOp(T v);

class GenDelegateDemo {
  // Return the summation of the argument.
  static int sum(int v) {
    int result = 0;
    for(int i=v; i>0; i--)
      result += i;

    return result;
  }

  // Return a string containing the reverse of the argument.
  static string reflect(string str) {
    string result = "";

    foreach(char ch in str)
      result = ch + result;

    return result;
  }

  public static void Main() {
    // Construct an int delegate. Notice use of method group
    // conversion on generic delegate.
    SomeOp intDel = sum;
    Console.WriteLine(intDel(3));

    // Construct a string delegate. Also use method group conversion.
    SomeOp strDel = reflect;
    Console.WriteLine(strDel("Hello"));
  }
}
The output is shown here:
6
olleH
Let’s look closely at this program. First, notice how the SomeOp delegate is declared:
delegate T SomeOp(T v);
Notice that T can be used as the return type even though the type parameter T is specified after the name SomeOp.
Inside GenDelegateDemo, the methods sum( ) and reflect( ) are declared, as shown here:
static int sum(int v) {

static string reflect(string str) {
The sum( ) method returns the summation of the integer value passed as an argument. The reflect( ) method returns a string that is the reverse of the string passed as an argument.
Inside Main( ), a delegate called intDel is instantiated and assigned a reference to sum( ):
SomeOp intDel = sum;
Because sum( ) takes an int argument and returns an int value, sum( ) is compatible with an int instance of SomeOp. Notice that the new C# 2.0 method group conversion syntax is used to assign sum to intDel. Method group conversions are fully compatible with generic delegates.
In similar fashion, the delegate strDel is created and assigned a reference to reflect( ):
SomeOp strDel = reflect;
Because reflect( ) takes a string argument and returns a string result, it is compatible with the string version of SomeOp.
Because of the type-safety inherent in generics, you cannot assign incompatible methods to delegates. For example, assuming the preceding program, the following statement would be in error:
SomeOp intDel = reflect; // Error!
Because reflect( ) takes a string argument and returns a string result, it cannot be assigned to an int version of SomeOp.
// Convert event example from Chapter 15 to
// use generic delegate.

using System;
// Derive a class from EventArgs.
class MyEventArgs : EventArgs {
  public int eventnum;
}

// Declare a generic delegate for an event.
delegate void MyEventHandler(T source, V args);

// Declare an event class.
class MyEvent {
  static int count = 0;

  public event MyEventHandler SomeEvent;

  // This fires SomeEvent.
  public void OnSomeEvent() {
    MyEventArgs arg = new MyEventArgs();

    if(SomeEvent != null) {
      arg.eventnum = count++;
      SomeEvent(this, arg);
    }
  }
}

class X {
  public void handler(T source, V arg) where V : MyEventArgs {
    Console.WriteLine("Event " + arg.eventnum +
                      " received by an X object.");
    Console.WriteLine("Source is " + source);
    Console.WriteLine();
  }
}

class Y {
  public void handler(T source, V arg) where V : MyEventArgs {
    Console.WriteLine("Event " + arg.eventnum +
                      " received by a Y object.");
    Console.WriteLine("Source is " + source);
    Console.WriteLine();
  }
}

class UseGenericEventDelegate {
  public static void Main() {
    X ob1 = new X();
    Y ob2 = new Y();
    MyEvent evt = new MyEvent();

    // Add handler() to the event list.
    evt.SomeEvent += ob1.handler;
    evt.SomeEvent += ob2.handler;
    // Fire the event.
    evt.OnSomeEvent();
    evt.OnSomeEvent();
  }
}
The output is shown here:
Event 0 received by an X object.
Source is MyEvent

Event 0 received by a Y object.
Source is MyEvent

Event 1 received by an X object.
Source is MyEvent

Event 1 received by a Y object.
Source is MyEvent

Using Multiple Constraints

There can be more than one constraint associated with a parameter. When this is the case, use a comma-separated list of constraints. In this list, the first constraint must be class or struct (if present), or the base class (if one is specified). It is illegal to specify both a class or struct constraint and a base class constraint. Next, must come any interface constraints. The new( ) constraint must be last. For example, this is a valid declaration:
class Gen where T : MyClass, IMyInterface, new() { // ...
In this case, T must be replaced by a type argument that inherits MyClass, implements IMyInterface, and has a parameterless constructor.
When using two or more type parameters, you can specify a constraint for each parameter by using a separate where clause. For example:
// Use multiple where clauses.

using System;

// Gen has two type arguments and both have
// a where clause.
class Gen where T : class
                where V : struct {
  T ob1;
  V ob2;

  public Gen(T t, V v) {
    ob1 = t;
    ob2 = v;
  }
}

class MultipleConstraintDemo {
  public static void Main() {
    // This is OK because string is a class and
    // int is a value type.
    Gen obj = new Gen("test", 11);

    // The next line is wrong because bool is not
    // a reference type.
//    Gen obj = new Gen(true, 11);

  }
}
In this example, Gen takes two type arguments, and both have a where clause. Pay special attention to its declaration:
class Gen where T : class
                where V : struct {

Using Constraint to Establish Relationship Between Two Type Parameters

One of the more interesting aspects of the base class constraint is that it allows you to establish a relationship between two type parameters. For example, consider the following generic class declaration:
class Gen<T, V> where V : T {
In this declaration, the where clause tells the compiler that V must inherit T. If this relationship is not present when an object of type Gen is declared, then a compile-time error will result. A constraint that uses a type parameter, such as that just shown, is called a naked type constraint. The following example illustrates this constraint:
// Create relationship between two type parameters.

using System;

class A {
  //...
}

class B : A {
  // ...
}

// Here, V must inherit T.
class Gen<T, V> where V : T {
  // ...
}

class NakedConstraintDemo {
  public static void Main() {

    // This declaration is OK because B inherits A.
    Gen<A, B> x = new Gen<A, B>();

    // This declaration is in error because
    // A does not inherit B.
//    Gen<B, A> y = new Gen<B, A>();

  }
}
First, notice that class B inherits class A. Next, examine the two Gen declarations in Main( ). As the comments explain, the first declaration:
Gen<A, B> x = new Gen<A, B>();
is legal because B inherits A. However, the second declaration:
//    Gen<B, A> y = new Gen<B, A>();
is illegal because A does not inherit B.

Using Multiple Constraints

There can be more than one constraint associated with a parameter. When this is the case, use a comma-separated list of constraints. In this list, the first constraint must be class or struct (if present), or the base class (if one is specified). It is illegal to specify both a class or struct constraint and a base class constraint. Next, must come any interface constraints. The new( ) constraint must be last. For example, this is a valid declaration:
class Gen<T> where T : MyClass, IMyInterface, new() { // ...
In this case, T must be replaced by a type argument that inherits MyClass, implements IMyInterface, and has a parameterless constructor.
When using two or more type parameters, you can specify a constraint for each parameter by using a separate where clause. For example:
// Use multiple where clauses.

using System;

// Gen has two type arguments and both have
// a where clause.
class Gen<T, V> where T : class
                where V : struct {
  T ob1;
  V ob2;

  public Gen(T t, V v) {
    ob1 = t;
    ob2 = v;
  }
}

class MultipleConstraintDemo {
  public static void Main() {
    // This is OK because string is a class and
    // int is a value type.
    Gen<string, int> obj = new Gen<string, int>("test", 11);

    // The next line is wrong because bool is not
    // a reference type.
//    Gen<bool, int> obj = new Gen<bool, int>(true, 11);

  }
}
In this example, Gen takes two type arguments, and both have a where clause. Pay special attention to its declaration:
class Gen<T, V> where T : class
                where V : struct {
Notice that the only thing that separates the first where clause from the second is whitespace. No other punctuation is required or valid.
Technorati :

Reference Type and Value Type Constraints

The last two constraints which explained in the previous posts enable you to indicate that a type argument must be either a reference type or a value type. These constraints are useful in the few cases where the difference between reference and value types is important to generic code. The general forms of the where statements for these constraints are shown here:

where T : class

where T : struct

Here, T is the name of the type parameter. When additional constraints are present, class or struct must be the first constraint in the list.

Here is an example that demonstrates the reference type constraint:

// Demonstrate a reference constraint.

using System;
class MyClass {
  //...
}

// Use a reference constraint.
class Test<T> where T : class {
  T obj;

  public Test() {
    // The following statement is legal only
    // because T is guaranteed to be a reference
    // type, which can be assigned the value null.
    obj = null;
  }

  // ...
}

class ClassConstraintDemo {
  public static void Main() {

    // The following is OK because MyClass is a class.
    Test<MyClass> x = new Test<MyClass>();

    // The next line is in error because int is
    // a value type.
//    Test<int> y = new Test<int>();
  }
}

First, notice how Test is declared:

class Test<T> where T : class {

The class constraint requires that any type argument for T be a reference type. In this program, this is necessary because of what occurs inside the Test constructor:

public Test() {
  // The following statement is legal only
  // because T is guaranteed to be a reference
  // type, which can be assigned the value null.
  obj = null;
}

Here, obj (which is of type T) is assigned the value null. This assignment is valid only for reference types. In C#, you cannot assign null to a value type. Therefore, without the constraint, the assignment would not have been valid, and the compile would have failed. This is one case in which the difference between value types and reference types might be important to a generic routine.

The value type constraint is the complement of the reference type constraint. It simply ensures that any type argument is a value type, including a struct or an enum. Here is an example:

// Demonstrate a value type constraint.

using System;

struct MyStruct {
  //...
}

class MyClass {
  // ...
}

class Test<T> where T : struct {
  T obj;

  public Test(T x) {
    obj = x;
  }

  // ...
}

class ValueConstraintDemo {
  public static void Main() {

    // Both of these declarations are legal.

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

    Test<int> y = new Test<int>(10);

    // But, the following declaration is illegal!
//    Test<MyClass> z = new Test<MyClass>(new MyClass());
  }
}

In this program, Test is declared as shown here:

class Test<T> where T : struct {

Because T of Test now has the struct constraint, T can be passed only value type arguments. This means that Test<MyStruct> and Test<int> are valid, but Test<MyClass> is not. To prove this, try removing the comment symbol from the start of the last line in the program and recompiling. An error will be reported.

Technorati :

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 :

Constrained Types in Generics

class Gen<T> {

any type can be passed to T. Thus, it is legal to create Gen objects in which T is replaced by int, double, string, FileStream, or any other type. Although having no restrictions on the type argument is fine for many purposes, sometimes it is useful to limit the types that can be passed to a type parameter. For example, you might want to create a method that operates on the contents of a stream, including a FileStream or MemoryStream. This situation seems perfect for generics, but you need some way to ensure that only stream types are used as type arguments. You don't want to allow a type argument of int, for example. You also need some way to tell the compiler that the methods defined by a stream will be available for use. For example, your generic code needs some way to know that it can call the Read( ) method.

To handle such situations, C# provides constrained types. When specifying a type parameter, you can specify a constraint that the type parameter must satisfy. This is accomplished through the use of a where clause when specifying the type parameter, as shown here:

class class-name<type-param> where type-param : constraints { // ...

Here, constraints is a comma-separated list of constraints.

There are five types of constraints:

  • You can require that a certain base class be present in a type argument by using a base class constraint. This constraint is specifi ed by naming the desired base class.

  • You can require that one or more interfaces be implemented by a type argument by using an interface constraint. This constraint is specified by naming the desired interface.

  • You can require that the type argument supply a parameterless constructor. This is called a constructor constraint. It is specified by new( ).

  • You can specify that a type argument must be a reference type by specifying the reference type constraint: class.

  • You can specify that the type argument be a value type by specifying the value type constraint: struct.

Of these constraints, the base class constraint and the interface constraint are probably the most often used, with the remaining adding fine-grained control. Each of the constraints is examined in detailed in next posts.

Using Explicit Type Arguments to Call Generic Method

Although implicit type inference is adequate for most invocations of a generic method, it is possible to explicitly specify the type argument. To do so, specify the type argument after the method name when calling the method. For example, here copyInsert( ) is explicitly passed type string:

ArrayUtils.copyInsert<string>("in C#", 1, strs, strs2);

A common reason for explicitly specifying the type when calling a generic method occurs when class hierarchies are involved. For example, assume the following classes:

class A {
  // ...
}
class B: A {
  // ...
}

Next, assuming the version of copyInsert( ) shown in the preceding program, you might try a sequence like the following to call copyInsert( ):

B[] b = { new B(), new B(), new B() };
A[] a = new A[4];

// Insert an A into an array of B, copying to an A array.
ArrayUtils.copyInsert(new A(), 1, b, a); // Error, ambiguous!

Here, the compiler cannot infer what type should be substituted for T. Should it be A or B? Remember, it is legal for a base class reference to refer to a derived class object. Thus, it is not inherently wrong to copy the contents of b into a. However, this works only if the type substituted for T is A, the base class. Unfortunately, the compiler doesn't know this.

To fix the situation, simply explicitly specify the type, as shown here:

ArrayUtils.copyInsert<A>(new A(), 1, b, a); // Fixed!

Here, A is explicitly substituted for T, and the line compiles and runs without error.

Creating Simple Generic Method

As the preceding posts have shown, methods inside a generic class can make use of a class's type parameter and are, therefore, automatically generic relative to the type parameter. However, it is possible to declare a generic method that uses one or more type parameters of its own. Furthermore, it is possible to create a generic method that is enclosed within a non-generic class.

Let's begin with an example. The following program declares a non-generic class called ArrayUtils and a static generic method within that class called copyInsert( ). The copyInsert( ) method copies the contents of one array to another, inserting a new element at a specified location in the process. It can be used with any type of array.

// Demonstrate a generic method.

using System;

// A class of array utilities.  Notice that this is not
// a generic class.
class ArrayUtils {

  // Copy an array, inserting a new element
  // in the process.  This is a generic method.
  public static bool copyInsert<T>(T e, int idx,
                                   T[] src, T[] target) {

    // See if target array is big enough.
    if(target.Length < src.Length+1)
      return false;

    // Copy src to target, inserting e at idx in the process.
    for(int i=0, j=0; i < src.Length; i++, j++) {
      if(i == idx) {
        target[j] = e;
        j++;
      }
      target[j] = src[i];
    }

    return true;
  }
}

class GenMethDemo {
  public static void Main() {
    int[] nums = { 1, 2, 3 };
    int[] nums2 = new int[4];

    // Display contents of nums.
    Console.Write("Contents of nums: ");
    foreach(int x in nums)
      Console.Write(x + " ");

    Console.WriteLine();

    // Operate on an int array.
    ArrayUtils.copyInsert(99, 2, nums, nums2);

    // Display contents of nums2.
    Console.Write("Contents of nums2: ");
    foreach(int x in nums2)
      Console.Write(x + " ");

    Console.WriteLine();

    // Now, use copyInsert on an array of strings.
    string[] strs = { "Generics", "are", "powerful."};
    string[] strs2 = new string[4];

    // Display contents of strs.
    Console.Write("Contents of strs: ");
    foreach(string s in strs)
      Console.Write(s + " ");

    Console.WriteLine();

    // Insert into a string array.
    ArrayUtils.copyInsert("in C#", 1, strs, strs2);

    // Display contents of strs2.
    Console.Write("Contents of strs2: ");
    foreach(string s in strs2)
      Console.Write(s + " ");

    Console.WriteLine();

    // This call is invalid because the first argument
    // is of type double, and the third and fourth arguments
    // have base types of int.
//    ArrayUtils.copyInsert(0.01, 2, nums, nums2);

  }
}

The output from the program is shown here:

Contents of nums: 1 2 3
Contents of nums2: 1 2 99 3
Contents of strs: Generics are powerful.
Contents of strs2: Generics in C# are powerful.

Let's examine copyInsert( ) closely. First, notice how it is declared by this line:

public static bool copyInsert<T>(T e, int idx,
                                 T[] src, T[] target) {

The type parameter is declared after the method name, but before the parameter list. Also notice that copyInsert( ) is static, enabling it to be called independently of any object. Understand, though, that generic methods can be either static or non-static. There is no restriction in this regard.

Now, notice how copyInsert( ) is called within Main( ) by use of the normal call syntax, without the need to specify type arguments. This is because the types of the arguments are automatically discerned, and the type of T is adjusted accordingly. This process is called type inference. For example, in the first call:

ArrayUtils.copyInsert(99, 2, nums, nums2);

the type of T becomes int because 99 and the base types of nums and nums2 are int. In the second call, string types are used, and T is replaced by string.

Now, notice the commented-out code, shown here:

//    ArrayUtils.copyInsert(0.01, 2, nums, nums2);

If you remove the comment symbol and then try to compile the program, you will receive an error. The reason is that the type of the first argument is double, but the base types of nums and nums2 are int. However, all three types must be substituted for the same type parameter, T. This causes a type mismatch, which results in a compile-time error. This ability to enforce type safety is one of the most important advantages of generic methods.

The syntax used to create copyInsert( ) can be generalized. Here is the general form of a generic method:

ret-type meth-name<type-parameter-list>(param-list) { // ...

In all cases, type-parameter-list is a comma-separated list of type parameters. Notice that for a generic method, the type parameter list follows the method name.

Creating Default Object of Type Parameter

When writing generic code, there will be times when the difference between value types and parameter types is an issue. One such situation occurs when you want to give an object of a type parameter a default value. For reference types, the default value is null. For non-struct value types, the default value is 0. The default value for a struct is an object of that struct with all fields set to their defaults. Thus, trouble occurs if you want to give a variable of a type parameter a default value. What value would you use-null, 0, or something else?

For example, given a generic class called Test declared like this:

class Test<T> {
  T obj;
  // ...

if you want to give obj a default value, would you use

obj = null; // works only for reference types

or

obj = 0; // works only for numeric types and enums, but not structs

Neither approach works in all cases.

The solution to this problem is to use another form of the default keyword, shown here:

default(type)

This produces a default value of the specified type, no matter what type is used. Thus, continuing with the example, to assign obj a default value of type T, you would use this statement:

obj = default(T);

This will work for all type arguments, whether they are value or reference types.

Here is a short program that demonstrates default:

// Demonstrate the default keyword.

using System;

class MyClass {
  //...
}

// Construct a default object of T.
class Test<T> {
  public T obj;

  public Test() {
    // The following statement would work
    // only for reference types.
//    obj = null;

    // This statement works for both
    // reference and value types.
    obj = default(T);
  }

  // ...
}

class DefaultDemo {
  public static void Main() {
    // Construct Test using a reference type.
    Test<MyClass> x = new Test<MyClass>();

    if(x.obj == null)
      Console.WriteLine("x.obj is null.");

    // Construct Test using a value type.
    Test<int> y = new Test<int>();

    if(y.obj == 0)
      Console.WriteLine("y.obj is 0.");
  }
}

The output is shown here:

x.obj is null.
y.obj is 0.

Filtering & sorting DataTables with DataTable.Select()

We all know that connections to databases from within our applications cost us processor cycles and thus time so it's a good idea to only 'talk' to your database when you really need too. In the .NET framework items like DataSets and DataTables etc. facilitate this to a large degree. In this post let's take the .Select() method of the DataTable class as evidence of this.

.NET framework 2.0 DataTable.Select() method
The DataTable.Select() method has four overloads and allows you to filter and display your rows in a number of ways in a manner similar to the way SQL does. The method can accept an expression string which although not nearly as powerful as full blown SQL does provide the advantage of not having to go back to the database to do simple things like showing records relevant to only a certain date range or a certain customer, product etc.

Below is some of the valid expressions and filters you can pop into the Select() method to narrow your record set.

Standard Operator based expressions
Things like >, <, >=, <=, =, <> are all supported of course. If your DataTable was called salesStaff and you only wanted to return staff with sales of greater than 100, you could do this with salesStaff.Select("sales > 100″). An array of DataRows will be returned, so depending on what you want to do with the results of the Select() method you may have to copy them back into another DataTable… more on that below.

More SQL query like expressions
Filtering by Like is also supported by the Select() method which I think is quite sweet. Wildcards as in SQL are * and %. Just like most variations of SQL support aggregate types so too does the Select() expression 'language'. Items like AVG, MIN, MAX, SUM and COUNT are all supported, so too are functions like LEN and SUBSTRING. If you need to test multple columns/conditions you can join them with the AND or OR operators but be careful if your using .Net 1.1 SP 1 as there was a documented bug in .Select() when it was used with AND.

Sorting with DataTable.Select()
This is probabely the main way I use the Select() method. Its supported not via the expression parameter but via the sort parameter which is available in two overloaded .Select() methods. Its format is columnName asc/desc.

Importing DataTable.Select() method results into another DataTable
Putting the results from a Select() query into another DataTable is often a requirement say for instance if you wanted to bind your results to a control. As mentioned above the Select() method returns an array of DataRows and since DataRows are the main building blocks of DataTables the process is very easy. Steps outlined are:

Issue .Clone() your source DataTable to create a new DataTable with the same schema
Issue .Select() on your source DataTable() to get an array of rows with the data you want
Loop through the DataRows array and issue .ImportRow() on your destination table each interation

and a code snippet to do the above might look something like the following:

//copy the schema of source table
DataTable above24 = dt.Clone();

//get only the rows you want
DataRow[] results = dt.Select("age > 24″);

//populate new destination table
foreach (DataRow dr in results)
above24.ImportRow(dr);

DataTable.Select() shortcomings
The main things I don't like about the .Select() method is how you have to go through intermediary steps to get your results into another DataTable, why can't it just return another DataTable (which is directly bindable to a load of .NET data controls) and its lack of support for selecting distinct/unique rows which is often needed. As as note on that last one, it is possible to return distinct rows in a DataTable using LINQwhich is a .NET 3.5 component, however that topic might best be served with another post at a later date.

Can't I just use a DataView instead of calling DataTable.Select()?
You can and DataViews are directly bindable to many controls too, however it is not always the best solution due to the generally accepted believe among many developers that .Select() is much faster than using the DataView equivalent of RowFilter (property). I regularly interchange between the two for a lot of small database projects, however for the projects where I need to be processing a mega amount of data I pretty much stick with .Select() 100% of the time as I reckon it provides real speed (as in seconds, not PC invisible micro time) advantages compared to DataViews.

Technorati :

Generic Structures

C# allows you to create generic structures. The syntax is the same as for generic classes. For example, in the following program, the XY structure, which stores X, Y coordinates, is generic:

// Demonstrate a generic struct.
using System;

// This structure is generic.
struct XY<T> {
  T x;
  T y;

  public XY(T a, T b) {
    x = a;
    y = b;
  }

  public T X {
    get { return x; }
    set { x = value; }
  }

  public T Y {
    get { return y; }
    set { y = value; }
  }

}

class StructTest {
  public static void Main() {
    XY<int> xy = new XY<int>(10, 20);
    XY<double> xy2 = new XY<double>(88.0, 99.0);
    Console.WriteLine(xy.X + ", " + xy.Y);

    Console.WriteLine(xy2.X + ", " + xy2.Y);
  }
}

The output is shown here:

10, 20
88, 99

Like generic classes, generic structures can have constraints. For example, this version of XY restricts type arguments to value types:

struct XY<T> where T : struct {
// ...

Technorati :

General Form of a Generic Class

The generics syntax shown in the preceding examples can be generalized. Here is the syntax for declaring a generic class:

class class-name<type-param-list> { // ...

Here is the syntax for declaring a reference to a generic class:

class-name<type-arg-list> var-name =
      new class-name<type-arg-list>(cons-arg-list);

Technorati :

Generic Class with Two Type Parameters

You can declare more than one type parameter in a generic type. To specify two or more type parameters, simply use a comma-separated list. For example, the following TwoGen class is a variation of the Gen class that has two type parameters:

// A simple generic class with two type
// parameters: T and V.

using System;
class TwoGen<T, V> {
  T ob1;
  V ob2;

  // Notice that this constructor has parameters
  // of type T and V.
  public TwoGen(T o1, V o2) {
    ob1 = o1;
    ob2 = o2;
  }

  // Show types of T and V.
  public void showTypes() {
    Console.WriteLine("Type of T is " + typeof(T));
    Console.WriteLine("Type of V is " + typeof(V));
  }

  public T getob1() {
    return ob1;
  }

  public V getob2() {
    return ob2;
  }
}

// Demonstrate two generic type parameters.
class SimpGen {
  public static void Main() {

    TwoGen<int, string> tgObj =
      new TwoGen<int, string>(119, "Alpha Beta Gamma");

    // Show the types.
    tgObj.showTypes();

    // Obtain and show values.
    int v = tgObj.getob1();
    Console.WriteLine("value: " + v);

    string str = tgObj.getob2();
    Console.WriteLine("value: " + str);
  }
}

The output from this program is shown here:

Type of T is System.Int32
Type of V is System.String
value: 119
value: Alpha Beta Gamma

Notice how TwoGen is declared:

class TwoGen<T, V> {

It specifies two type parameters: T and V, separated by a comma. Because it has two type parameters, two type arguments must be passed to TwoGen when an object is created, as shown next:

TwoGen<int, string> tgObj =
  new TwoGen<int, string>(119, "Alpha Beta Gamma");

In this case, int is substituted for T, and string is substituted for V.

Although the two type arguments differ in this example, it is possible for both types to be the same. For example, the following line of code is valid:

TwoGen<string, string> x = new TwoGen<string, string>("Hello", "Goodbye");

In this case, both T and V would be of type string. Of course, if the type arguments were always the same, then two type parameters would be unnecessary.

How Generics Improve Type Safety

At this point you might be asking yourself the following question: Given that the same functionality found in the generic Gen class can be achieved without generics, by simply specifying object as the data type and employing the proper casts, what is the benefit of making Gen generic? The answer is that generics automatically ensure the type safety of all operations involving Gen. In the process, generics eliminate the need for you to use casts and to type-check code by hand.

To understand the benefits of generics, first consider the following program that creates a non-generic equivalent of Gen:

// NonGen is functionally equivalent to Gen
// but does not use generics.

using System;

class NonGen {
  object ob; // ob is now of type object

  // Pass the constructor a reference of
  // type object.
  public NonGen(object o) {
    ob = o;
  }

  // Return type object.
  public object getob() {
    return ob;
  }

  // Show type of ob.
  public void showType() {
    Console.WriteLine("Type of ob is " + ob.GetType());
  }
}

// Demonstrate the non-generic class.
class NonGenDemo {
  public static void Main() {
    NonGen iOb;
    // Create NonGen object.
    iOb = new NonGen(102);

    // Show the type of data stored in iOb.
    iOb.showType();

    // Get the value in iOb.
    // This time, a cast is necessary.
    int v = (int) iOb.getob();
    Console.WriteLine("value: " + v);

    Console.WriteLine();

    // Create another NonGen object and
    // store a string in it.
    NonGen strOb = new NonGen("Non-Generics Test");

    // Show the type of data stored in strOb.
    strOb.showType();

    // Get the value of strOb.
    // Again, notice that a cast is necessary.
    String str = (string) strOb.getob();
    Console.WriteLine("value: " + str);

    // This compiles, but is conceptually wrong!
    iOb = strOb;

    // The following line results in a runtime exception.
    // v = (int) iOb.getob(); // runtime error!
  }
}

This program produces the following output:

Type of ob is System.Int32
value: 102

Type of ob is System.String
value: Non-Generics Test

As you can see, the output is similar to the previous version of the program.

There are several things of interest in this version. First, notice that NonGen replaces all uses of T with object. This makes NonGen able to store any type of object, as can the generic version. However, it also prevents the compiler from having any real knowledge about the type of data actually stored in NonGen, which is bad for two reasons. First, explicit casts must be employed to retrieve the stored data. Second, many kinds of type mismatch errors cannot be found until runtime. Let's look closely at each problem.

First, notice this line:

int v = (int) iOb.getob();

Because the return type of getob( ) is now object, the cast to int is necessary to enable the value returned by getob( ) to be unboxed and stored in v. If you remove the cast, the program will not compile. In the generic version of the program, this cast was not needed because int was passed as a type argument when iOb was constructed. In the non-generic version, the cast must be employed. This is not only an inconvenience, but a potential source of error.

Now, consider the following sequence from near the end of the program:

// This compiles, but is conceptually wrong!
iOb = strOb;

// The following line results in a runtime exception.
// v = (int) iOb.getob(); // runtime error!

Here, strOb is assigned to iOb. However, strOb refers to an object that contains a string, not an integer. This assignment is syntactically valid because all NonGen references are the same, and any NonGen reference can refer to any other NonGen object. However, the statement is semantically wrong, as the commented-out line shows. In that line, the return type of getob( ) is cast to int, and then an attempt is made to assign this value to v. The trouble is that iOb now refers to an object that stores a string, not an int. Unfortunately, without use of generics, the compiler has no way to know this. Instead, a runtime exception will occur when the cast to int is attempted. To see this for yourself, try removing the comment symbol from the start of the line, and then compiling and running the program. A runtime error will occur.

The preceding sequence can't occur when generics are used. If this sequence were attempted in the generic version of the program, the compiler would catch it and report an error, thus preventing a serious bug that results in a runtime exception. The ability to create type-safe code in which type-mismatch errors are caught at compile time is a key advantage of generics. Although using object references to create "generic" code has always been possible in C#, that code was not type-safe, and its misuse could result in runtime exceptions. Generics prevent this from occurring. In essence, through generics, what were once runtime errors have become compile-time errors. This is a major advantage.

There is one other point of interest in the NonGen program. Notice how the type of the NonGen instance variable ob is obtained by showType( ):

Console.WriteLine("Type of ob is " + ob.GetType());

Generics Means?

At its core, the term generics means parameterized types. Parameterized types are important because they enable you to create classes, interfaces, methods, and delegates in which the type of data operated on is specified as a parameter. Using generics, it is possible to create a single class, for example, that automatically works with different types of data. A class, interface, method, or delegate that operates on a parameterized type is called generic, as in generic class or generic method.

It is important to understand that C# has always given you the ability to create generalized classes, interfaces, methods, and delegates by operating through references of type object. Because object is the base class of all other classes, an object reference can refer to any type object. Thus, in pre-generics code, generalized code used object references to operate on a variety of different types of objects. The problem was that it could not do so with type safety.

Generics add the type safety that was lacking. They also streamline the process because it is no longer necessary to employ casts to translate between object and the type of data that is actually being operated upon. Thus, generics expand your ability to reuse code and let you do so safely and easily.

Simple Generics Example

Let's begin with a simple example of a generic class. The following program defines two classes. The first is the generic class Gen, and the second is GenDemo, which uses Gen.

// A simple generic class.

using System;

// In the following Gen class, T is a type
// parameter that will be replaced by a real
// type when an object of type Gen is created.
class Gen<T> {
  T ob; // declare an object of type T

  // Notice that this constructor has a parameter of type T.
  public Gen(T o) {
    ob = o;
  }

  // Return ob, which is of type T.
  public T getob() {
    return ob;
  }

  // Show type of T.
  public void showType() {
    Console.WriteLine("Type of T is " + typeof(T));
  }
}

// Demonstrate the generic class.
class GenDemo {
  public static void Main() {
    // Create a Gen reference for int.
    Gen<int> iOb;

    // Create a Gen<int> object and assign its
    // reference to iOb.
    iOb = new Gen<int>(102);

    // Show the type of data used by iOb.
    iOb.showType();

    // Get the value in iOb.
    int v = iOb.getob();
    Console.WriteLine("value: " + v);

    Console.WriteLine();
    // Create a Gen object for strings.
    Gen<string> strOb = new Gen<string>("Generics add power.");
    
    // Show the type of data stored in strOb.
    strOb.showType();
    
    // Get the value in strOb.
    string str = strOb.getob();
    Console.WriteLine("value: " + str);
  }
}

The output produced by the program is shown here:

Type of T is System.Int32
value: 102

Type of T is System.String
value: Generics add power.

Let's examine this program carefully.

First, notice how Gen is declared by the following line:

class Gen<T> {

Here, T is the name of a type parameter. This name is used as a placeholder for the actual type that will be passed to Gen when an object is created. Thus, T is used within Gen whenever the type parameter is needed. Notice that T is contained within < >. This syntax can be generalized. Whenever a type parameter is being declared, it is specified within angle brackets. Because Gen uses a type parameter, Gen is a generic class.

In the declaration of Gen, there is no special significance to the name T. Any valid identifier could have been used, but T is traditional. Other commonly used type parameter names include V and E. Of course, you can also use descriptive names for type parameters, such as TValue or TKey. When using a descriptive name, it is common practice to use T as the first letter.

Next, T is used to declare a variable called ob, as shown here:

T ob; // declare an object of type T

As explained, T is a placeholder for the actual type that will be specified when a Gen object is created. Thus, ob will be a variable of the type passed to T. For example, if type string is passed to T, then in that instance, ob will be of type string.

Now consider Gen's constructor:

public Gen(T o) {
  ob = o;
}

Notice that its parameter, o, is of type T. This means that the actual type of o is determined by the type passed to T when a Gen object is created. Also, because both the parameter o and the instance variable ob are of type T, they will both be of the same actual type when a Gen object is created.

The type parameter T can also be used to specify the return type of a method, as is the case with the getob( ) method, shown here:

public T getob() {
  return ob;
}

Because ob is also of type T, its type is compatible with the return type specified by getob( ).

The showType( ) method displays the type of T by passing T to the typeof operator. Because a real type will be substituted for T when an object of type Gen is created, typeof will obtain type information about the actual type.

The GenDemo class demonstrates the generic Gen class. It first creates a version of Gen for type int, as shown here:

Gen<int> iOb;

Look closely at this declaration. First, notice that the type int is specified within the angle brackets after Gen. In this case, int is a type argument that is passed to Gen's type parameter, T. This creates a version of Gen in which all uses of T are replaced by int. Thus, for this declaration, ob is of type int, and the return type of getob( ) is of type int.

When you pass a type argument to a generic class, you are creating what is referred to in C# as a closed constructed type. (The term closed indicates that a type argument has been specified.) Thus, Gen<int> is a closed constructed type. In essence, a generic type, such as Gen<T>, is an abstraction. It is only after a specific version, such as Gen<int>, has been constructed that a concrete type has been created. In C# terminology, a construct such as Gen<T> is called an open constructed type, because no type argument has been specified.

The next line assigns to iOb a reference to an instance of an int version of the Gen class:

iOb = new Gen<int>(102);

Notice that when the Gen constructor is called, the type argument int is also specified. This is necessary because the type of the object (in this case iOb) to which the reference is being assigned is of type Gen<int>. Thus, the reference returned by new must also be of type Gen<int>. If it isn't, a compile-time error will result. For example, the following assignment will cause a compile-time error:

iOb = new Gen<double>(118.12); // Error!

Because iOb is of type Gen<int>, it can't be used to refer to an object of Gen<double>. This type checking is one of the main benefits of generics because it ensures type safety.

The program then displays the type of ob within iOb, which is System.Int32. This is the .NET class that corresponds to int. Next, the program obtains the value of ob by use of the following line:

int v = iOb.getob();

Because the return type of getob( ) is T, which was replaced by int when iOb was declared, the return type of getob( ) is also int. Thus, this value can be assigned to an int variable.

Next, GenDemo declares an object of type Gen<string>:

Gen<string> strOb = new Gen<string>("Generics add power.");

Because the type argument is string, string is substituted for T inside Gen. This creates a string version of Gen, as the remaining lines in the program demonstrate.

Technorati :

C# Executing SQL Command or Stored Procedure

The IDbCommand interface represents a database command, and each data provider includes a unique implementation. Here is the list of IDbCommand implementations for the five standard data providers.

  • System.Data.Odbc.OdbcCommand

  • System.Data.OleDb.OleDbCommand

  • System.Data.OracleClient.OracleCommand

  • System.Data.SqlServerCe.SqlCeCommand

  • System.Data.SqlClient.SqlCommand

To execute a command against a database you must have an open connection and a properly configured command object appropriate to the type of database you are accessing. You can create command objects directly using a constructor, but a simpler approach is to use the CreateCommand factory method of a connection object. The CreateCommand method returns a command object of the correct type for the data provider and configures it with basic information obtained from the connection you used to create the command. Before executing the command, you must configure the properties described in the below table which are common to all command implementations.

Table 10.3: Common Command Object Properties

Property

Description

CommandText

A string containing the text of the SQL command to execute or the name of a stored procedure. The content of the CommandText property must be compatible with the value you specify in the CommandType property.

CommandTimeout

An int that specifies the number of seconds to wait for the command to return before timing out and raising an exception. Defaults to 30 seconds.

CommandType

A value of the System.Data.CommandType enumeration that specifies the type of command represented by the command object. For most data providers, valid values are StoredProcedure, when you want to execute a stored procedure, and Text, when you want to execute a SQL text command. If you are using the OLE DB Data Provider, you can specify TableDirect when you want to return the entire contents of one or more tables; refer to the .NET Framework SDK documentation for more details. Defaults to Text.

Connection

An IDbConnection instance that provides the connection to the database on which you will execute the command. If you create the command using the IDbConnection.CreateCommand method, this property will be automatically set to the IDbConnection instance from which you created the command.

Parameters

A System.Data.IDataParameterCollection instance containing the set of parameters to substitute into the command.

Transaction

A System.Data.IDbTransaction instance representing the transaction into which to enlist the command. (See the .NET Framework SDK documentation for details about transactions.)

Once you have configured your command object, there are a number of ways to execute it depending on the nature of the command, the type of data returned by the command, and the format in which you want to process the data.

To execute a command, such as INSERT, DELETE, or CREATE TABLE, that doesn't return database data, call ExecuteNonQuery. For the UPDATE, INSERT, and DELETE commands, ExecuteNonQuery method returns an int that specifies the number of rows affected by the command. For other commands, such as CREATE TABLE, ExecuteNonQuery returns the value -1. Here is an example that uses UPDATE to modify a record.

public static void ExecuteNonQueryExample(IDbConnection con) {

    // Create and configure a new command.
    IDbCommand com = con.CreateCommand();
    com.CommandType = CommandType.Text;
    com.CommandText = "UPDATE Employees SET Title = 'Sales Director'" +
        " WHERE EmployeeId = '5'";

    // Execute the command and process the result.
    int result = com.ExecuteNonQuery();

    if (result == 1) {
        Console.WriteLine("Employee title updated.");
    } else {
        Console.WriteLine("Employee title not updated.");
    }
}

To execute a command that returns a result set, such as a SELECT statement or stored procedure, use the ExecuteReader method. ExecuteReader returns an IDataReader instance through which you have access to the result data. Most data providers also allow you to execute multiple SQL commands in a single call to the ExecuteReader method. This code excerpt uses the ExecuteReader method to execute the Ten Most Expensive Products stored procedure from the Northwind database and display the results to the console.

public static void ExecuteReaderExample(IDbConnection con) {

    // Create and configure a new command.
    IDbCommand com = con.CreateCommand();
    com.CommandType = CommandType.StoredProcedure;
    com.CommandText = "Ten Most Expensive Products";

    // Execute the command and process the results
    using (IDataReader reader = com.ExecuteReader()) {

        Console.WriteLine("Price of the Ten Most Expensive Products.");

        while (reader.Read()) {

            // Display the product details.
            Console.WriteLine("  {0} = {1}", 
                reader["TenMostExpensiveProducts"],
                reader["UnitPrice"]);
        }
    }
}

If you want to execute a query but only need the value from the first column of the first row of result data, use the ExecuteScalar method. The value is returned as an object reference that you must cast to the correct type. Here is an example.

public static void ExecuteScalarExample(IDbConnection con) {

    // Create and configure a new command.
    IDbCommand com = con.CreateCommand();
    com.CommandType = CommandType.Text;
    com.CommandText = "SELECT COUNT(*) FROM Employees";

    // Execute the command and cast the result.
    int result = (int)com.ExecuteScalar();

    Console.WriteLine("Employee count = " + result);
}

Technorati :

C# Connection Pooling

Connection pooling significantly reduces the overhead associated with creating and destroying database connections. Connection pooling also improves the scalability of solutions by reducing the number of concurrent connections a database must maintain-many of which sit idle for a significant portion of their lifetimes. With connection pooling, instead of creating and opening a new connection object whenever you need one, you take an already open connection from the connection pool. When you have finished using the connection, instead of closing it, you return it to the pool and allow other code to use it.

The SQL Server and Oracle data providers encapsulate connection- pooling functionality that they enable by default. One connection pool exists for each unique connection string you specify when you open a new connection. Each time you open a new connection with a connection string that you have used previously, the connection is taken from the existing pool. Only if you specify a different connection string will the data provider create a new connection pool. You can control some characteristics of your pool using the connection string settings described in below table. Note that Once created, a pool exists until your process terminates.

Table 10.2: Connection String Settings That Control Connection Pooling

Setting

Description

Connection Lifetime

Specifies the maximum time in seconds that a connection is allowed to live in the pool before it's closed. The age of a connection is tested only when the connection is returned to the pool. This setting is useful for minimizing pool size if the pool isn't heavily used and also ensures optimal load balancing is achieved in clustered database environments. The default value is 0, which means connections exist for the life of the current process.

Connection Reset

Supported only by the SQL Server data provider. Specifies whether connections are reset as they are taken from the pool. A value of "True" ensures a connection's state is reset but requires an additional communication with the database. The default value is "True".

Max Pool Size

Specifies the maximum number of connections that should be in the pool. Connections are created and added to the pool as required until this figure is reached. If a request for a connection is made but there are no free connections, the caller will block until a connection becomes available. The default value is 100.

Min Pool Size

Specifies the minimum number of connections that should be in the pool. On pool creation, this number of connections are created and added to the pool. During periodic maintenance, or when a connection is requested, connections are added to the pool to ensure the minimum number of connections is available. The default value is 0.

Pooling

Set to "False" to obtain a non-pooled connection. The default value is "True".

This code excerpt from the sample code for this post demonstrates the configuration of a connection pool that contains a minimum of 5 and a maximum of 15 connections. Connections expire after 10 minutes (600 seconds) and are reset each time a connection is obtained from the pool.

// Obtain a pooled connection.
using (SqlConnection con = new SqlConnection()) {

    // Configure the SqlConnection object's connection string.
    con.ConnectionString = 
        "Data Source = localhost;" +    // local SQL Server instance
        "Database = Northwind;" +       // the sample Northwind DB
        "Integrated Security = SSPI;" + // integrated Windows security
        "Min Pool Size = 5;" +          // configure minimum pool size
        "Max Pool Size = 15;" +         // configure maximum pool size
        "Connection Reset = True;" +    // reset connections each use
        "Connection Lifetime = 600";    // set maximum connection lifetime

    // Open the Database connection.
    con.Open();

    // Access the database...
    §

    // At the end of the using block, the Dispose calls Close, which
    // returns the connection to the pool for reuse.
}

This code excerpt demonstrates how to use the Pooling setting to obtain a connection object that isn't from a pool. This is useful if your application uses a single long-lived connection to a database.

// Obtain a non-pooled connection.
using (SqlConnection con = new SqlConnection()) {

    // Configure the SqlConnection object's connection string.
    con.ConnectionString = 
        "Data Source = localhost;" +    // local SQL Server instance
        "Database = Northwind;" +       // the sample Northwind DB
        "Integrated Security = SSPI;" + // integrated Windows security
        "Pooling = False";              // specify non-pooled connection

    // Open the Database connection.
    con.Open();

    // Access the database...
    

    // At the end of the using block, the Dispose calls Close, which
    // closes the non-pooled connection.
}

The ODBC and OLE DB data providers also support connection pooling, but they don't implement connection pooling within managed .NET classes and you don't configure the pool in the same way as for the SQL Server or Oracle data providers. ODBC connection pooling is managed by the ODBC Driver Manager and configured using the ODBC Data Source Administrator tool in the Control Panel. OLE DB connection pooling is managed by the native OLE DB implementation; the most you can do is disable pooling by including the setting "OLE DB Services=-4;" in your connection string. The SQL Server CE data provider doesn't support connection pooling because SQL Server CE supports only a single concurrent connection.

Connecting to DB

The first step in database access is to open a connection to the database. The IDbConnection interface represents a database connection, and each data provider includes a unique implementation. Here is the list of IDbConnection implementations for the five standard data providers.

  • System.Data.Odbc.OdbcConnection

  • System.Data.OleDb.OleDbConnection

  • System.Data.OracleClient.OracleConnection

  • System.Data.SqlServerCe.SqlCeConnection

  • System.Data.SqlClient.SqlConnection

You configure a connection object using a connection string. A connection string is a set of semicolon-separated name value pairs. You can supply a connection string either as a constructor argument or by setting a connection object's ConnectionString property before opening the connection. Each connection class implementation requires that you provide different information in the connection string. Refer to the ConnectionString property documentation for each implementation to see the values you can specify. Possible settings include the following:

  • The name of the target database server

  • The name of the database to open initially

  • Connection timeout values

  • Connection-pooling behavior

  • Authentication mechanisms to use when connecting to secured databases, including provision of user names and passwords

Once configured, call the connection object's Open method to open the connection to the database. You can then use the connection object to execute commands against the data source. The properties of a connection object also allow you to retrieve information about the state of a connection and the settings used to open the connection. When you're finished with a connection, you should always call its Close method to free up the underlying database connection and system resources. IDbConnection extends System.IDisposable, meaning that each connection class implements the Dispose method. Dispose automatically calls Close, making the using statement a very clean and efficient way of using connection objects in your code.

You achieve optimum scalability by opening your database connection as late as possible and closing it again as soon as you have finished. This ensures that you don't tie up database connections for long periods and give all code the maximum opportunity to obtain a connection. This is especially important if you are using connection pooling.

The code shown here demonstrates how to use the SqlConnection class to open a connection to a SQL Server database running on the local machine that uses integrated Windows security. To access a remote machine, simply change the data source name from localhost to the name of your database instance.

// Create an empty SqlConnection object.
using (SqlConnection con = new SqlConnection()) {

    // Configure the SqlConnection object's connection string.
    con.ConnectionString = 
        "Data Source = localhost;"+ // local SQL Server instance
        "Database = Northwind;" +   // the sample Northwind DB
        "Integrated Security=SSPI"; // integrated Windows security

    // Open the Database connection.
    con.Open();

    // Display information about the connection.
    if (con.State == ConnectionState.Open) {
        Console.WriteLine("SqlConnection Information:");
        Console.WriteLine("  Connection State = " + con.State);
        Console.WriteLine("  Connection String = " + 
            con.ConnectionString);
        Console.WriteLine("  Database Source = " + con.DataSource);
        Console.WriteLine("  Database = " + con.Database);
        Console.WriteLine("  Server Version = " + con.ServerVersion);
        Console.WriteLine("  Workstation Id = " + con.WorkstationId);
        Console.WriteLine("  Timeout = " + con.ConnectionTimeout);
        Console.WriteLine("  Packet Size = " + con.PacketSize);
    } else {
        Console.WriteLine("SqlConnection failed to open.");
        Console.WriteLine("  Connection State = " + con.State);
    }
    // At the end of the using block Dispose() calls Close().
}

As another example, the following excerpt from the sample code for this recipe shows the connection string used to open a connection to the same database if you were using the OLE DB data provider to provide connectivity.

// Create an empty OleDbConnection object.
using (OleDbConnection con = new OleDbConnection()) {

    // Configure the OleDbConnection object's connection string.
    con.ConnectionString = 
        "Provider = SQLOLEDB;" +         // OLE DB Provider for SQL Server
        "Data Source = localhost;" +     // local SQL Server instance
        "Initial Catalog = Northwind;" + // the sample Northwind DB
        "Integrated Security=SSPI";      // integrated Windows security

    // Open the Database connection.
    con.Open();
}

Technorati :

Archives

LocalsAdda.com-Variety In Web World

Fun Mail - Fun in the Mail