23

I'm writing a class that does essentially the same type of calculation for each of the primitive numeric types in C#. Though the real calculation is more complex, think of it as a method to compute the average of a number of values, e.g.

class Calc
{
    public int Count { get; private set; }
    public int Total { get; private set; }
    public int Average { get { return Count / Total; } }
    public int AddDataPoint(int data)
    {
        Total += data;
        Count++;
    }
}

Now to support that same operation for double, float and perhaps other classes that define operator + and operator /, my first thought was to simply use generics:

class Calc<T>
{
    public T Count { get; private set; }
    public T Total { get; private set; }
    public T Average { get { return Count / Total; } }
    public T AddDataPoint(T data)
    {
        Total += data;
        Count++;
    }
}

Unfortunately C# is unable to determine whether T supports operators + and / so does not compile the above snippet. My next thought was to constrain T to types that support those operators, but my initial research indicates this cannot be done.

It's certainly possible to box each of the types I want to support in a class that implements a custom interface e.g. IMath and restrict T to that, but this code will be called a great number of times and I want to avoid boxing overhead.

Is there an elegant and efficient way to solve this without code duplication?

Eric J.
  • 147,927
  • 63
  • 340
  • 553

4 Answers4

15

I ended up using Expressions, an approach outlined by Marc Gravell that I found by following links off of spinon's comment.

https://jonskeet.uk/csharp/miscutil/usage/genericoperators.html

tukaef
  • 9,074
  • 4
  • 29
  • 45
Eric J.
  • 147,927
  • 63
  • 340
  • 553
6

(excuse me if I post it today, but I was looking for a place where to put this piece of code, and this question seemed to be perfect)

As an extension on the Gravell's article:

public static class Add<T>
{
    public static readonly Func<T, T, T> Do;

    static Add()
    {
        var par1 = Expression.Parameter(typeof(T));
        var par2 = Expression.Parameter(typeof(T));

        var add = Expression.Add(par1, par2);

        Do = Expression.Lambda<Func<T, T, T>>(add, par1, par2).Compile();
    }
}

You use it like:

int sum = Add<int>.Do(x, y);

The advantage is that we use the type system of .NET for safekeeping the various "variants" of Add and creating new ones if necessary. So the first time you call Add<int>.Do(...) the Expression will be built, but if you call it a second time, the Add<int> will already be fully initialized.

On some simple benchmark, it's 2x slower than direct addition. I think it's very good. Ah... it's compatible with objects that redefine the operator+. Clearly building the other operations is easy.

Addition from Meirion Hughes

Method can be extended with meta-coding so you can handle cases of T1 operation T2. For instance, here if T1 is a number, then it needs to be converted to T2 == double first before the operator * then converts it back. Whereas when T1 is Foo and Foo has operator to multiply with a T2 == double you can omit the conversion. The try, catch is necessary because it is the easiest way to check if the T operator *(T, double) is present.

public static class Scale<T>
{
    public static Func<T, double, T> Do { get; private set; }

    static Scale()
    {
        var par1 = Expression.Parameter(typeof(T));
        var par2 = Expression.Parameter(typeof(double));

        try
        {
            Do = Expression
                .Lambda<Func<T, double, T>>(
                    Expression.Multiply(par1, par2),
                    par1, par2)
                .Compile();
        }
        catch
        {
            Do = Expression
                .Lambda<Func<T, double, T>>(
                    Expression.Convert(
                        Expression.Multiply(
                            Expression.Convert(par1, typeof (double)),
                            par2),
                        typeof(T)),
                    par1, par2)
                .Compile();
        }
    }
}
xanatos
  • 109,618
  • 12
  • 197
  • 280
  • @Meirion I don't like very much what you did (adding by editing)... But the code was neat. I've re-edited a little and changed a little the explanation – xanatos Apr 13 '15 at 10:02
  • yeah I know, sorry. I thought it would be useful (it was for me), and I was about to add it as separate answer, but this thread is locked and the other thread would be off-topic. :/ – Meirion Hughes Apr 13 '15 at 10:49
  • @MeirionHughes Just the next time, put your name on the added piece of text, like I did *Addition from Meirion Hughes*, so it is clear what is from the author 1 and what is from the author 2 – xanatos Apr 13 '15 at 11:09
2

There is an approach using dynamic in C# 4.0, it is not perfect obviously but it can bring a new light to the matter.

Details are in this blog post

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Johann Blais
  • 9,389
  • 6
  • 45
  • 65
  • The trouble with 'dynamic' is that it introduces another level of indirection. I'm performing a large number of complex calculations and suspect (but don't know, to be honest) that the indirection will have a measurable impact on overall app performance. – Eric J. Jan 16 '12 at 20:11
1

I found another interesting approach, which is easier to code and debug than the expression tree solution I originally used:

http://www.codeproject.com/KB/cs/genericnumerics.aspx

This solution uses generic type constraints in an interesting way to ensure all required operations are supported, but without introducing any boxing or virtual method calls.

Eric J.
  • 147,927
  • 63
  • 340
  • 553