2012-06-08 11 views
7

Ist es möglich, grundlegende Arithmetik (mindestens Addition) in C# Generika zu implementieren, wie Sie können with C++ templates? Ich habe seit einiger Zeit versucht, sie aufzusetzen und zu arbeiten, aber C# lässt Sie den gleichen generischen Typ nicht mehrfach deklarieren, wie Sie es mit Templates können.Implementieren von Arithmetik in Generika?

Umfangreiches Google-Suchen lieferte keine Antwort.

EDIT: Danke, aber was ich suche, ist eine Möglichkeit, die Arithmetik zur Kompilierzeit zu machen, etwas wie Kirchenziffern in Generiktypen einzubetten. Deshalb habe ich den Artikel, den ich gemacht habe, verlinkt. Arithmetische in generische Typen, keine Arithmetik in Instanzen von generische Typen.

+3

Leider ist die Typeinschränkung nicht zulassen, dass Sie verlangen, dass der Typ arithmetische Operatoren unterstützt. Was ich interessant finde, ist, dass im BCL-Quellcode für z.B. 'Int32' finden Sie eine' IArithmetic ' Schnittstelle in der Vererbungsliste, die auskommentiert ist. Dies ist reine Spekulation meinerseits, aber wenn Microsoft diese Schnittstelle in der BCL aktiviert, dann könnten Sie vielleicht 'IArithmetic 'als eine Einschränkung angeben, um Ihnen zu erlauben, Ihre eigenen generischen Klassen mit Arithmetik zu schreiben. –

+0

Link zu ähnlicher Frage: http://stackoverflow.com/q/4039694/613130 ​​ – xanatos

Antwort

4

Leider können Sie arithmetische Operationen auf generische Typen verwenden

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

wird nicht funktionieren in C#!

Aber Sie können Ihre eigenen numerischen Typen erstellen und die Operatoren überladen (arithmetische Gleichheit und implicit, explicit). So können Sie ganz natürlich mit ihnen arbeiten. Sie können jedoch keine Vererbungshierarchie mit Generika erstellen. Sie müssen eine nicht generische Basisklasse oder Schnittstelle verwenden.

Ich habe es gerade mit einem Vektor-Typ gemacht.Eine verkürzte Version hier:

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

Was meinst du mit "' struct "Einschränkungen sind nicht erlaubt? –

+0

Sie können tatsächlich. Siehe [Einschränkungen für Typparameter (C# -Programmierhandbuch)] (http://msdn.microsoft.com/en-us/library/d5x73970.aspx). –

+0

@AdamHouldsworth: Structs können Schnittstellen implementieren! Da Strukturen nicht voneinander erben können, wäre die Beschränkung auf einen bestimmten Strukturtyp derselbe, als ob überhaupt kein generischer Parameter vorhanden wäre (oder Sie hätten genau einen gültigen generischen Typparameter, nämlich diese eine Struktur)! –

3

Bitte zögern Sie nicht, mehr Klarheit zu bieten, wenn meine Antwort off-kilter scheint.

Es gibt keine generischen Einschränkungen für Operatoren in der Sprache C#. Wie Jon Skeet mit Unconstrained Melody bewiesen hat, könnten die Constraints in der CLR selbst durchaus perfekt sein.

Das Beste, was Sie mit Constraints tun können, ist die Bereitstellung von Schnittstellen/benutzerdefinierten Klassen, die die von Ihnen benötigten Aktionen aufzeigen. Sie könnten das primitive Element nicht bereitstellen (es sei denn, Sie implementieren auch den Operator implicit), aber Sie können zumindest generischen Code für den mathematischen Teil erstellen.

Generische Constraints erlauben dem Compiler, die verfügbaren Member basierend auf dem kleinsten gemeinsamen Nenner (wie durch die Einschränkung oder das Fehlen von angegeben) zu folgern. In den meisten Fällen sind Generika nicht eingeschränkt und geben Ihnen nur die Semantik object.


Alternativ, vermeiden Einschränkungen und verwenden dynamic vorübergehend die generische Variable zu speichern und dann die Annahme, (über Duck Typing), dass sie die betreffenden Betreiber hat:

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

Das das DLR beinhaltet und muss etwas Performance-Overhead (ich habe keine genauen Zahlen), insbesondere wenn Sie die Berechnungen als performancekritisch betrachten.


Ich bin mir nicht sicher, was Sie „mehrere Male die gleichen generischen Typ deklarieren“ bedeuten, das funktioniert:

class Tuple<T1, T2> // etc. 

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

Freunde, die intuitive Antwort auf diese Frage in C# RTTI ist und hin und her von der Objektklasse

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

Ich bin ein Anfänger in C# und jede einfache Erklärung ist wichtig, daher wenn jemand abstimmt, ich würde mich freuen Kommentar dazu! Könnte jemand (sonst) bitte erklären, warum diese Antwort abgelehnt wurde? Warum ist dieser Ansatz nicht korrekt? – Celdor

+1

@Celdor: Zuerst wird es langsam, weil Sie RTTI machen und Strings vergleichen. Zweitens, es ist nicht wirklich generisch, wenn ich jeden Typ manuell hinzufügen muss ... –

+1

Vor kurzem hat der Jit die Optimierung eingeführt, die Typeof (T) erkennt und tote Zweige basierend auf RTTI-Bedingungen entfernt. Diese Lösung kann mit geeigneten Modifikationen schneller als andere Antworten ausgeführt werden. – Luca

-1

ja, es kann durch die Verwendung dynamischer Typ Variablen Gießen erfolgen .

Beispiel:

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

für weitere Referenz bitte click here