posted by Rüdiger Klaehn on Wed 6th Oct 2004 20:31 UTC

".NET Generics, Page 3/3"
Syntax candy

One small issue with this approach is that you still can not use operator overloading on type parameters. Instead of simply writing sum+=list[i]; in the above example, we have to call the Add method of the calculator struct like this: sum=calculator.Add(sum,list[i]);. This is usually not a big deal, but it is a problem that can be solved by using a wrapper struct:

public struct Number<T,C>
    where C:ICalculator<T<,new()
{
    private T value;
    private static C calculator=new C();
    private Number(T value) {
        this.value=value;
    }
    public static implicit operator Number<T,C>(T a) 
    {
        return new Number<T,C>(a);
    }
    public static implicit operator T(Number<T,C> a) 
    {
        return a.value;
    }
    public static Number<T,C> operator + (Number<T,C> a, Number<T,C> b)
    {
        return calculator.Add(a.value,b.value);
    }
    ...other operators...
}

By using this wrapper struct, we can rewrite the Lists<T,C> class to use operators again:

class Lists<T,C>
    where T:new() 
    where C:IAdder<T>,new();
{
    public static T Sum(List<T> list)
    {
        Number<T,C> sum=new T();
        for(int i=0;i<list.Count;i++)
            sum+=list[i];
        return sum;
    }
}

Unfortunately, due to some very serious limitations in the current JIT compiler inlining heuristics the version using the wrapper struct will be slower than the version that calls calculator.Add directly. But since the wrapper struct does not add any dynamic method invocation the calls to wrapper methods could easily be optimized away by a more capable JIT compiler.

Conclusion

Using valuetypes as type parameters, it is possible to write efficient classes that do arithmetic operations on type parameters. The code is not as short as it would be in C++, but that is acceptable considering the large benefits of constrained type parameters. The thing to keep in mind is that while casting a valuetype to an interface has a significant overhead due to boxing and dynamic method invocation, constraining a type parameter by an interface has no such overhead.

References

Benchmark of generic vs. nongeneric summation of list elements
Interfaces for all arithmetic calculations
Implementation of the above interfaces for the primitive types
Wrapper classes for the interfaces
A sample program that uses the above interfaces to calculate the standard deviation of a List<T>
Another creative way to use zero-size structs
Please vote on this suggestion

About the Author
Rüdiger Klaehn works as freelance programmer. He is currently trying to start a company with some friends, but in germany that is a long and tedious process. He is very interested in functional programming languages and hopes that .NET will lead to a more widespread adoption in the industry.
If you would like to see your thoughts or experiences with technology published, please consider writing an article for OSNews.
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