2012-06-08 14 views
7

È possibile implementare l'aritmetica di base (almeno l'aggiunta) nei generici C#, come si può with C++ templates? Ho cercato per un po 'di farli funzionare e funzionare, ma C# non ti permette di dichiarare lo stesso tipo generico più volte, come è possibile con i modelli.Implementare l'aritmetica in generici?

Il googling esteso non ha fornito una risposta.

EDIT: Grazie, ma quello che sto cercando è un modo per fare l'aritmetica in fase di compilazione, incorporando qualcosa come numeri di Chiesa in tipi di generici. Ecco perché ho collegato l'articolo che ho fatto. Aritmetica in tipi generici, non aritmetica in caso di tipi generici.

+3

Purtroppo, il vincolo di tipo non consente di richiedere che il tipo di supporti operatori aritmetici. Quello che trovo interessante è che nel codice sorgente BCL per es. 'Int32' troverai un'interfaccia' IArithmetic 'nell'elenco di eredità che è commentato. Questa è pura speculazione da parte mia, ma se Microsoft ha abilitato quell'interfaccia nel BCL, potresti forse specificare 'IArithmetic ' come un vincolo per consentire di scrivere le tue classi generiche con l'aritmetica. –

+0

Link a domande simili: http://stackoverflow.com/q/4039694/613130 ​​ – xanatos

risposta

4

Purtroppo si può usare operazioni aritmetiche sui tipi generici

T Add(T a, T b) 
{ 
    return a + b; // compiler error here 
} 

non funzionerà in C#!

Ma è possibile creare i propri tipi numerici e sovraccaricare gli operatori (uguaglianza aritmetica e implicit, explicit). Questo ti permette di lavorare con loro in un modo abbastanza naturale. Tuttavia non è possibile creare una gerarchia di eredità con generici. Dovrai utilizzare una classe base o un'interfaccia non generica.

L'ho appena fatto con un tipo vettoriale.Una versione ridotta qui:

public class Vector 
{ 
    private const double Eps = 1e-7; 

    public Vector(double x, double y) 
    { 
     _x = x; 
     _y = y; 
    } 

    private double _x; 
    public double X 
    { 
     get { return _x; } 
    } 

    private double _y; 
    public double Y 
    { 
     get { return _y; } 
    } 

    public static Vector operator +(Vector a, Vector b) 
    { 
     return new Vector(a._x + b._x, a._y + b._y); 
    } 

    public static Vector operator *(double d, Vector v) 
    { 
     return new Vector(d * v._x, d * v._y); 
    } 

    public static bool operator ==(Vector a, Vector b) 
    { 
     if (ReferenceEquals(a, null)) { 
      return ReferenceEquals(b, null); 
     } 
     if (ReferenceEquals(b, null)) { 
      return false; 
     } 
     return Math.Abs(a._x - b._x) < Eps && Math.Abs(a._y - b._y) < Eps; 
    } 

    public static bool operator !=(Vector a, Vector b) 
    { 
     return !(a == b); 
    } 

    public static implicit operator Vector(double[] point) 
    { 
     return new Vector(point[0], point[1]); 
    } 

    public static implicit operator Vector(PointF point) 
    { 
     return new Vector(point.X, point.Y); 
    } 

    public override int GetHashCode() 
    { 
     return _x.GetHashCode()^_y.GetHashCode(); 
    } 

    public override bool Equals(object obj) 
    { 
     var other = obj as Vector; 
     return other != null && Math.Abs(other._x - _x) < Eps && Math.Abs(other._y - _y) < Eps; 
    } 

    public override string ToString() 
    { 
     return String.Format("Vector({0:0.0000}, {1:0.0000})", _x, _y); 
    } 
} 
+0

Che cosa intendi "i vincoli di' struct' non sono consentiti "? –

+0

In realtà puoi. Vedere [Vincoli sui parametri del tipo (C# Programming Guide)] (http://msdn.microsoft.com/en-us/library/d5x73970.aspx). –

+0

@AdamHouldsworth: le strutture possono implementare le interfacce! Dal momento che le struct non possono ereditare l'una dall'altra, restringere a un certo tipo di struct sarebbe lo stesso di non avere affatto un parametro generico (o avresti esattamente un parametro di tipo generico valido, ovvero questa una struct)! –

3

Non esitate a offrire ulteriori chiarimenti se la mia risposta sembra fuori luogo.

Non ci sono limiti generici sugli operatori nel linguaggio C#, almeno. Come Jon Skeet ha dimostrato con Unconstrained Melody, i vincoli potrebbero effettivamente essere perfettamente validi nel CLR stesso.

Il meglio che si può fare con i vincoli è fornire interfacce/classi personalizzate che espongono le azioni necessarie. Non saresti in grado di fornire la primitiva (a meno che tu non implementi anche l'operatore implicit), ma ti consentirebbe almeno di creare codice generico per la parte matematica.

I vincoli generici consentono al compilatore di dedurre i membri disponibili in base al minimo comune denominatore (come specificato dal vincolo o dalla mancanza di). Il più delle volte, i generici non sono vincolati e quindi ti danno solo la semantica object.


alternativa, evitare l'uso di vincoli e utilizzare dynamic per memorizzare temporaneamente la variabile generico e poi fare l'ipotesi (tramite digitazione anatra) che ha gli operatori interessati:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var result = Add<int, long, float>(1, 2); 
     Console.WriteLine(result); // 3 
     Console.WriteLine(result.GetType().FullName); // System.Single 
     Console.Read(); 
    } 

    static T3 Add<T1, T2, T3>(T1 left, T2 right) 
    { 
     dynamic d1 = left; 
     dynamic d2 = right; 
     return (T3)(d1 + d2); 
    } 
} 

Ciò comporta la DLR e avrà alcuni overhead delle prestazioni (non ho cifre esatte), soprattutto se si intende che i calcoli siano critici dal punto di vista delle prestazioni.


io non sono sicuro di quello che vuoi dire "dichiara lo stesso tipo generico più volte", questo funziona:

class Tuple<T1, T2> // etc. 

var myTuple = new Tuple<int, int>(1, 2); 
0

amici, la risposta intuitiva per questo in C# è RTTI e lanciando avanti e indietro dalla classe oggetto

enter code here 

class MyMath 
{ 
    public static T Add<T>(T a, T b) where T: struct 
    { 
     switch (typeof(T).Name) 
     { 
      case "Int32": 
       return (T) (object)((int)(object)a + (int)(object)b); 
      case "Double": 
       return (T)(object)((double)(object)a + (double)(object)b); 
      default: 
       return default(T); 
     } 
    } 
} 

class Program 
{ 
    public static int Main() 
    { 
     Console.WriteLine(MyMath.Add<double>(3.6, 2.12)); 
     return 0; 
    } 
} 
+0

Sono un principiante in C# e qualsiasi semplice spiegazione è importante quindi se qualcuno voterà, mi farebbe piacere commentarlo! Ora, potrebbe qualcuno (altro), per favore, spiegare perché questa risposta ha il voto basso? Perché questo approccio non è corretto? – Celdor

+1

@Celdor: in primo luogo, sarà lento, perché stai facendo RTTI e confrontando le stringhe. In secondo luogo, non è veramente generico se devo aggiungere manualmente ogni tipo ... –

+1

Recentemente il jit ha introdotto il optmization che rileva typeof (T) e rimuove i rami morti in base alle condizioni di RTTI. Questa soluzione, con le opportune modifiche, può essere eseguita più velocemente di altre risposte. – Luca

-1

sì, può essere fatto utilizzando le variabili di tipo dinamico .

esempio:

T Add(T value1, T value2) 
{   
     dynamic a = value1; 
     dynamic b = value2; 
     return (a + b); 
} 

per più di riferimento si prega di click here