2013-06-10 20 views
6

Mi chiedo se esiste un modo, in C#, per avere un tipo basato su un tipo primitivo, e duplicarlo per usare invece altre primitive.Discussione sul tipo numerico generico in C#

So che non è molto chiaro, non so davvero come spiegarlo (e l'inglese non è la mia lingua madre, mi dispiace per quello ...), quindi cercherò di spiegarlo usando pseudo- codice.

Un esempio veloce:

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

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

Il problema è che mi piacerebbe avere un galleggiante e una versione Int32 di questo tipo.

quello che sto facendo in realtà (valido codice C#):

//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); 
    } 
} 

Funziona ma non è "sexy", e in caso di ampie calchi che utilizzano i metodi AsXXX, ci saranno inevitabilmente un impatto sulla spettacoli.

Idealmente, avrei Arithmetics methodes su tutti e tre i tipi, ma sarebbe un dolore per mantenere ...

Quale sarebbe la soluzione ideale (pseudo-codice, non è valida 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 
} 

so che questo non è possibile in C# per il momento, ma qui è la vera domanda:

avete qualche idea su come gestire bene e in modo efficiente questo?

Quello che ho pensato di (pseudo-codice, non è valida C#): Codice

//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 
} 

In questo modo, non avrei duplicato, più facile da mantenere

attesa di vostra idee :)

PS: Se un moderatore ha idee su come formattare meglio la mia domanda, sentitevi liberi di modificarla :)

+1

sto pensando 'Vector2 dove T: IConvertible' – Jodrell

+0

Ci sono modi solo la ricerca "[C#] [] generici numeri" e si ottiene più risultati. Ma avrà un impatto grave sulle prestazioni. E considerando che vuoi lavorare con i vettori, la prestazione è la prima preoccupazione qui. – Euphoric

+1

possibile duplicato di [C#: Interfaccia generica per numeri] (http://stackoverflow.com/questions/1325131/c-generico-interfaccia-per-numeri) – Euphoric

risposta

5

Ecco un approccio molto bello dato dal @ mike-z, utilizzando modelli T4:

Il modello (di file .tt):

<#@ 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 #>... 
    } 

<#+ } #> 

e ha generato il codice di auto:

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 generico, quindi nessun impatto sulle prestazioni, codice scritto una volta ...

+0

Ora l'unica piccola cosa sbagliata con quel metodo, è la mancanza di evidenziazione di sintassi quando si lavora sul file .tt, ma dato che il file .cs viene generato automaticamente ogni volta che si salva il file .tt, e il file .cs, ha evidenziazione sintassi ... – ingham

+2

Questa è la linea di quello che stavo pensando. Aggiungerò solo che potresti voler esaminare le classi parziali (o le structs, qui) e i metodi parziali se vuoi personalizzare il codice generato. –

4

si potrebbe fare qualcosa di simile,

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); 
    } 
} 

ma, se non si desidera un trattamento generico. Quindi potresti anche avere diversi tipi specializzati Vector2.

+0

Approccio molto interessante. Quale sarebbe l'impatto delle prestazioni sull'uso intensivo? – ingham

+0

@ingham le conversioni devono rallentare le cose. Generici in generale non hanno un impatto sulle prestazioni. – Jodrell

0

Ho pensato alla stessa soluzione suggerita da Jodrell. Voglio solo aggiungere:

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); 
}