Thursday, June 24, 2010

Generics

If the operations performed by several overloaded methods are identical for each argument type, the overloaded methods can be more compactly and conveniently coded using a generic method. You can write a single generic method declaration that can be called at different times with arguments of different types. Based on the types of the arguments passed to the generic method, the compiler handles each method call appropriately.

public T Max <T>(T val1, T val2) where T : IComparable  {
    T retVal = val2;
    if (val2.CompareTo(val1) < 0)
        retVal = val1;
    return retVal;
}

And the Calling Would be like :
double doubleMax = Max <double>(3939.99, 39999.99);
int intMax = Max <int>(339, 23);
string stringMax = Max <string>("AAAA", "BBBBBB");


You can use generics in classes and in structs. Here is a useful generic point struct:

public struct Point <T>
{
   
   public T X;
   
   public T Y;
}
You can use the generic point for integer coordinates, for example:

Point <int> point;
point.X = 1;
point.Y = 2;
Or for charting coordinates that require floating point precision:

Point <double> point;
point.X = 1.2;
point.Y = 3.4;
Inheritance and Generics

When deriving from a generic base class, you must provide a type argument instead of the base-class's generic type parameter:
public class BaseClass <T>
{...}
public class SubClass : BaseClass <int>
{...}
If the subclass is generic, instead of a concrete type argument, you can use the subclass generic type parameter as the specified type for the generic base class:

public class SubClass <T> : BaseClass <T> 
{...}

When using the subclass generic type parameters, you must repeat any constraints stipulated at the base class level at the subclass level. For example, derivation constraint:

public class BaseClass <T>  where T : ISomeInterface 
{...}
public class SubClass <T> : BaseClass <T> where T : ISomeInterface
{...}
Or constructor constraint:

public class BaseClass <T>  where T : new()
{   
   public T SomeMethod()
   {
      return new T();
   }
}
public class SubClass <T> : BaseClass <T> where T : new() 
{...}

A base class can define virtual methods whose signatures use generic type parameters. When overriding them, the subclass must provide the corresponding types in the method signatures:

public class BaseClass <T>
{ 
   public virtual T SomeMethod()
   {...}
}
public class SubClass: BaseClass <int>
{ 
   public override int SomeMethod()
   {...}
}
If the subclass is generic it can also use its own generic type parameters for the override:

public class SubClass <T>: BaseClass <T>
{ 
   public override T SomeMethod()
   {...}
}

You can define generic interfaces, generic abstract classes, and even generic abstract methods. These types behave like any other generic base type:

public interface ISomeInterface <T>
{
   T SomeMethod(T t);
}
public abstract class BaseClass <T>
{
   public abstract T SomeMethod(T t);
}

public class SubClass <T> : BaseClass <T>
{
   public override T SomeMethod(T t) 
   {...)
}
There is an interesting use for generic abstract methods and generic interfaces. In C# 2.0, it is impossible to use operators such as + or += on generic type parameters. For example, the following code does not compile because C# 2.0 does not have operator constraints:

public class Calculator <T>
{
   public T Add(T arg1,T arg2)
   {
      return arg1 + arg2;//Does not compile 
   }
   //Rest of the methods 
}


Nonetheless, you can compensate using abstract methods (or preferably interfaces) by defining generic operations. Since an abstract method cannot have any code in it, you can specify the generic operations at the base class level, and provide a concrete type and implementation at the subclass level:

public abstract class BaseCalculator <T>
{
   public abstract T Add(T arg1,T arg2);
   public abstract T Subtract(T arg1,T arg2);
   public abstract T Divide(T arg1,T arg2);
   public abstract T Multiply(T arg1,T arg2);
}
public class MyCalculator : BaseCalculator <int>
{
   public override int Add(int arg1, int arg2)
   {
      return arg1 + arg2;
   }
   //Rest of the methods 
} 
A generic interface will yield a somewhat cleaner solution as well:

public interface ICalculator <T>
{
   T Add(T arg1,T arg2);
   //Rest of the methods 
}
public class MyCalculator : ICalculator <int>
{
   public int Add(int arg1, int arg2)
   {
      return arg1 + arg2;
   }
   //Rest of the methods 
}

0 comments:

Post a Comment