A discussion of a typical numeric type in C #

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.

I know this is not very clear, I don’t know how to explain it (and English is not my native language, sorry for that ...), so I will try to explain it using pseudocode.

Quick example:

public struct Vector2d { public double X; public double Y; //Arithmetic methods: Length, normalize, rotate, operators overloads, etc... } 

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

What I am actually doing (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); } } 

This works, but it is not “sexy,” and in the case of extensive tricks using AsXXX methods, it will inevitably affect performance.

Ideally, I would have had arithmetic methods for all three types, but it would be painful to maintain ...

What would be the perfect solution (pseudo-code, invalid C #):

 public struct Vector2<T> where T : numeric { public TX; public TY; public T Length { return (T)Math.Sqrt(X * X + Y * Y); } //Other arithmetic methods } 

I know this is not possible in C # at the moment, but here is the true question:

Do you have any ideas on how to handle this beautifully and effectively?

What I was thinking about (pseudo code, invalid 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 } 

This way I would not have duplicated code, it’s easier to maintain

Waiting for your ideas :)

PS: If the moderator has ideas on how best to format my question, feel free to edit it :)

+6
source share
3 answers

Here is a very good approach @ mike-z gives using T4 templates:

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 automatically 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... } } 

There is no common, therefore, no impact on performance, code written once ...

+5
source

You can 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 TX { get { return ConvertBack(this.x); } } public TY { get { return ConvertBack(this.y); } } private static T ConvertBack(double d) { return (t)Convert.ChangeType(d, typeof(T), CultureInfo.CurrentCulture); } } 

but if you do not want a general treatment. Then you can have several specialized Vector2 types.

+4
source

I thought of the same decision as Jodrell. Just want to add:

 private Vector2(double x, double y) { this.x = x; this.y = y; } public Vector2<E> ToVector<E>() where E : IConvertible { return new Vector2<E>(x, y); } 
0
source

Source: https://habr.com/ru/post/946936/


All Articles