Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Discussion on generic numeric type in C#

Tags:

c#

generics

I wonder if there is a way, in C#, to have a type based on a primitive type, and duplicate it to use other primitives instead.

I know it's not very clear, I don't really know how to explain this (and english is not my mother-tongue, sorry for that...), so I'll try to explain it using pseudo-code.

A quick example:

public struct Vector2d {
    public double X;
    public double Y;

    //Arithmetic methods: Length, normalize, rotate, operators overloads, etc...
}

The problem is that I'd like to have a float and a int32 version of this type.

What i'm doing actually (valid C# code):

//This type contains all useful methods
public struct Vector2d {
    public float X;
    public float Y;

    public Vector2f AsFloat() {
        return new Vector2f((float)X, (float)Y);
    }
    public Vector2f AsInteger() {
        return new Vector2i((int)X, (int)Y);
    }

    //Arithmetic methods
}

//These types are only here to be casted from/to, all the arithmetics methods are on the double-based type
public struct Vector2f {
    public float X;
    public float Y;

    public Vector2f AsDouble() {
        return new Vector2d(X, Y);
    }
    public Vector2f AsInteger() {
        return new Vector2i((int)X, (int)Y);
    }
}
public struct Vector2i {
    public int X;
    public int Y;

    public Vector2f AsFloat() {
        return new Vector2f(X, Y);
    }
    public Vector2f AsDouble() {
        return new Vector2d(X, Y);
    }
}

It works but it's not "sexy", and in case of extensive casts using the AsXXX methods, there will inevitably be an impact on performances.

Ideally, I would have arithmetics methodes on all of three types, but it would be a pain to maintain...

What would be the ideal solution (pseudo-code, not valid C#):

public struct Vector2<T> where T : numeric {
    public T X;
    public T Y;

    public T Length {
        return (T)Math.Sqrt(X * X + Y * Y);
    }
    //Other arithmetic methods
}

I know this isn't possible in C# for the moment, but here is the true question:

Do you have any idea on how to nicely and efficiently handle this ?

What I've been thinking to (pseudo-code, not valid C#) :

//The TYPE defined constant should be used by Vector2Body instead of plain typed "double", "float", etc...

public struct Vector2d {
    #define TYPE double
    #import Vector2Body
}

public struct Vector2f {
    #define TYPE float
    #import Vector2Body
}

public struct Vector2i {
    #define TYPE int
    #import Vector2Body
}

That way, I wouldn't have duplicated code, easier to maintain

Waiting for your ideas :)

PS: If a moderator have ideas on how to better format my question, feel free to edit it :)

like image 241
ingham Avatar asked Jun 10 '13 09:06

ingham


2 Answers

Here is a very nice approach given by @mike-z , using T4 templates:

The template (.tt file):

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
using System;

namespace My.Math {
<# Vector2Body("d", "double"); #>
<# Vector2Body("f", "float"); #>
<# Vector2Body("i", "int"); #>
}

<#+ private void Vector2Body(string suffix, string t) { #>

    public struct Vector2<#= suffix #> {
        public <#= t #> X;
        public <#= t #> Y;

        //Arithmetic for <#= t #>...
    }

<#+ } #>

And the auto generated code:

using System;

namespace My.Math {

    public struct Vector2d {
        public double X;
        public double Y;

        //Arithmetic for double...
    }


    public struct Vector2f {
        public float X;
        public float Y;

        //Arithmetic for float...
    }


    public struct Vector2i {
        public int X;
        public int Y;

        //Arithmetic for int...
    }

}

No generic, so no performance impact, code written once...

like image 158
ingham Avatar answered Sep 20 '22 05:09

ingham


You could do something like,

public struct Vector2<T> where T : IConvertible
{
    private readonly double x;
    private readonly double y;

    public Vector2(T x, T y)
    {
        this.x = x.ToDouble(CultureInfo.CurrentCulture);
        this.y = y.ToDouble(CultureInfo.CurrentCulture);
    }

    public T X
    {
        get
        {
            return ConvertBack(this.x);
        }
    }

    public T Y
    {
        get
        {
            return ConvertBack(this.y);
        }
    }

    private static T ConvertBack(double d)
    {
        return (t)Convert.ChangeType(d, typeof(T), CultureInfo.CurrentCulture); 
    }
}

but, if you don't want generic treatment. Then you might as well have several specialized Vector2 types.

like image 44
Jodrell Avatar answered Sep 22 '22 05:09

Jodrell