posted by Rüdiger Klaehn on Wed 6th Oct 2004 20:31 UTC
IconThe current implementation of generics in .NET 2.0 does a very good job to make typed collections faster and more easy to use. But as many people have noticed, it leaves much to be desired when you want to do calculations with generic types.

The Problem

In the last article about this topic I used a Point<T> structure to illustrate this problem. But of course the problem exists in many other situations as well. For example you might want to write a method that calculates the sum of all elements of a List<T> where T is a type like int, double, decimal or any other type that can be summed.

For someone used to C++ templates this looks like a trivial problem. He would probably come up with something like this:

public class Lists {
    ...
    public static T Sum(List<T> list) 
    {
        T sum=0;
        for(int i=0;i<list.Count;i++)
            sum+=list[i];
        return sum;
    }
    ...
}

This method could then be used to calculate sums:

List<int> list1;
List<float> list2;
List<decimal> list3;
...
int sum1=Lists.Sum(list1);
float sum2=Lists.Sum(list2);
decimal sum3=Lists.Sum(list3);

But surprisingly enough this is not possible with .NET generics. The problem is that in .NET type parameters without constraints are assumed to be of type System.Object, which is not very useful to do calculations. There is currently no way to constrain type parameters in such a way as to require the existence of a static method. And since operators in .NET are static methods it is not possible to have operator constraints.

A clean way to enable numerical computations would be to let the basic data types like int, float, double, decimal etc. implement an interface for arithmetic operations. Then this interface could be used to constrain the type parameters. This would work similar to the IComparable<T> interface that all basic data types implement.

Unfortunately the people at microsoft seem to be unable to do this in time for .NET 2.0, which will probably be released in 2005.

Many people have been thinking about this problem, among them Anders Hejlsberg and Eric Gunnerson. There are many creative solutions, such as this, which involves dynamic code generation.

As an example, lets take a look at the solution suggested by Anders Hejlsberg. I copied the code from Eric Gunnersons blog.

  • First define an abstract base class Calculator<T> for the operations to be performed:
    public abstract class Calculator<T>
    {
        public abstract T Add(T a, T b); 
    } 
    
  • Then specialize for the types we want to use:
    namespace Int32
    {
        public class Calculator: Calculator
        {
            public override int Add(int a, int b)
            {
                return a + b;
            }
        } 
    } 
    
  • Then use an appropriate Calculator for the type you want to use
    class AlgorithmLibrary<T> where T: new() 
    {
        Calculator<T> calculator;
     
        public AlgorithmLibrary(Calculator<T> calculator)
        {
            this.calculator = calculator;
        } 
    
        public T Sum(List<T> items)
        {
            T sum = new T(); 
    
    
            for (int i = 0; i < items.Count; i++)
            {
                sum = calculator.Add(sum, items[i]);
            } 
            return sum;
        }
    } 
    

All those implementations solve the problem, but unfortunately they all involve some kind of dynamic method invocation (virtual methods, interfaces or even delegates), which makes them quite slow. A virtual method adding two integers will spend only a small fraction of the time doing actual work. Most time will be used for the virtual method call itself. For most numerical applications, the overhead associated with virtual method calls and such is simply unacceptable.

Table of contents
  1. ".NET Generics, Page 1/3"
  2. ".NET Generics, Page 2/3"
  3. ".NET Generics, Page 3/3"
e p (0)    32 Comment(s)

Technology White Papers

See More