2010-12-01 7 views
22

Ich bin kürzlich über das Problem, dass ich wollte, dass eine Funktion sowohl Doppel-und Ganzzahlen arbeiten und fragte mich, warum gibt es keine gemeinsame Schnittstelle für alle Zahl Typen (mit arithmetischen Operatoren und Vergleiche).Warum teilen sich Zahlentypen nicht eine gemeinsame Schnittstelle?

Es würde Schreiben Funktionen wie Math.Min (die in einer Gazillion Überladungen bestehen) viel bequemer machen.

Wäre die Einführung einer zusätzlichen Schnittstelle eine bahnbrechende Änderung?

Edit: Ich denke über diese wie

public T Add<T>(T a, T b) where T: INumber 
{ 
    return a+b; 
} 

oder

public T Range<T>(T x, T min, T max) where T:INumber 
{ 
    return Max(x, Min(x, max), min); 
} 
+0

@Saeed: Das ist der Punkt. =) Meine Add-Methode ist nur ein einfaches Beispiel. In meinem Fall mache ich eine grundlegende statistische Analyse für ein gegebenes Array von Zahlen und es ist mir egal, ob es sich um int, double, decimal oder BigIntegers handelt, solange ich sie hinzufügen kann. (und müssen diese Methoden in vielen Überladungen nicht schreiben) – Jens

+0

https://referencesource.microsoft.com/#mscorlib/system/int32.cs,31 - 'IArithmetic ' scheint auskommentiert zu sein, leider :( – Caramiriel

Antwort

8

Wenn Sie solche "generische" Arithmetik machen wollen, ist Ihre Option in einer stark typisierten Sprache wie C# ziemlich begrenzt. Marc Gravell described the problem as follows:

.NET 2.0 eingeführt Generika in die .NET-Welt, die die Tür für viele elegante Lösungen für bestehende Probleme öffnete. Generische Constraints können verwendet werden, um die Typargumente auf bekannte Interfaces etc. zu beschränken, um den Zugriff auf Funktionalität zu gewährleisten - oder für einfache Gleichheits-/Ungleichheitstests die Comparer<T>.Default und EqualityComparer<T>.Default Singletons implementieren bzw. IEqualityComparer<T> (erlaubt uns beispielsweise das Sortieren von Elementen ohne zu haben etwas über das betreffende "T" wissen).

Bei all dem gibt es immer noch eine große Lücke, wenn es um Betreiber geht. Da Operatoren als statische Methoden deklariert sind, gibt es keine IMath<T> oder eine ähnliche äquivalente Schnittstelle, die alle numerischen Typen implementieren; und in der Tat würde die Flexibilität der Betreiber es sehr schwer machen, dies sinnvoll zu tun. Schlimmer: Viele der Operatoren primitiver Typen existieren nicht einmal als Operatoren; stattdessen gibt es direkte IL-Methoden. Um die Situation noch komplexer zu machen, erfordert Nullable <> das Konzept der "hebelten Operatoren", wobei das innere "T" die Operatoren beschreibt, die für den Nullable-Typ gelten - dies ist jedoch als Sprachfunktion implementiert und wird nicht von der Laufzeit (macht Reflektion noch mehr Spaß).

jedoch, C# 4.0 eingeführt, um die dynamic Schlüsselwort, das Sie die richtige Überlastung zur Laufzeit verwenden können, wählen:

using System; 

public class Program 
{ 
    static dynamic Min(dynamic a, dynamic b) 
    { 
     return Math.Min(a, b);   
    } 

    static void Main(string[] args) 
    { 
     int i = Min(3, 4); 
     double d = Min(3.0, 4.0); 
    } 
} 

Sie sollten sich bewusst sein, dass diese Art Sicherheit entfernt und Sie können Ausnahmen zur Laufzeit erhalten Wenn die dynamische Runtime keine geeignete Überlast zum Aufrufen finden kann, z weil du Typen gemischt hast.

Wenn Sie Typsicherheit erhalten möchten, sollten Sie sich die Klassen in der MiscUtil Bibliothek ansehen, die generische Operatoren für grundlegende Operationen bereitstellen.

Bitte beachten Sie, dass Sie, wenn Sie nur nach bestimmten Operationen sind, möglicherweise die Schnittstellen verwenden, die die integrierten Typen bereits implementieren. Zum Beispiel kann eine typsichere generic Min Funktion könnte wie folgt aussehen:

public static T Min<T>(params T[] values) where T : IComparable<T> 
{ 
    T min = values[0]; 
    foreach (var item in values.Skip(1)) 
    { 
     if (item.CompareTo(min) < 0) 
      min = item; 
    } 
    return min; 
} 
+2

Ihre Verallgemeinerung guter Ansatz, aber es unterstützt nicht zum Beispiel spezifische 'Add' Verfahren, ist es nützlich nur für vergleichbare Aktionen, nicht für alle möglichen Aktionen in Zahlen und dynamisch ist nicht guter Ansatz wegen seiner Geschwindigkeit. –

+0

Verwenden Sie dynamisch ehrlich ist das einfachste Beispiel hier. Obwohl, wenn Sie nicht garantieren, dass Sie nur numerische Typen in Ihrem Design übergeben, wird es einen hässlichen Absturz haben. Obwohl ein solcher Konstruktionsfehler höchstwahrscheinlich ein schwerwiegender Fehler sein würde und von Ihren Unit-Tests trotzdem gefunden wird. – rolls

1

Nun verwenden, können Sie nicht Operatoren in Schnittstellen definieren sowieso und Strukturen (obwohl sie Schnittstellen unterstützen) wouldn Es funktioniert nicht gut über Schnittstellenimplementierungen, da dies Boxing und Unboxing erfordern würde, was natürlich ein großer Leistungshit wäre, wenn mathematische Operationen rein durch Schnittstellenimplementierungen durchgeführt würden.

ich auch hervorheben möchte, dass, wenn Sie eine Struktur auf seine Schnittstelle umgewandelt, das Ergebnis Objekt einen Referenztyp ist (eine Box-Objekt), die Sie Operationen auf, nicht die ursprüngliche Struktur selbst durchführen:

interface IDoSomething 
{ 
    void DoSomething(); 
} 

struct MyStruct : IDoSomething 
{ 
    public MyStruct(int age) 
    { 
    this.Age = age; 
    } 

    public int Age; 

    pubblic void DoSomething() 
    { 
    Age++; 
    } 
} 

public void DoSomething(IDoSomething something) 
{ 
    something.DoSomething(); 
} 

Wenn Ich übergebe meine Instanz meiner Struktur, ihre Boxed (wird ein Referenztyp), die ich meine DoSomething Operation ausführen, aber meine ursprüngliche Instanz meiner Struktur wird nicht geändert.

+0

es wird tatsächlich funktionieren, solange Sie auf eine Schnittstelle ‚werfen‘, wird die Struktur geschachtelt werden. – leppie

+0

Siehe meine Antwort, für das, was ich meine. – leppie

+0

ich bin auf eine Schnittstelle Casting, wenn ich die 'DoSomething (ISomething etwas) nennen . 'Methode –

-3

Das ist das Hauptmerkmal einer "stark typisierten Sprache". Dies ist etwas, das Milliarden von Fehlern pro Minute vermeidet. Natürlich wollen wir ganz anders sein als doppelt.

+2

mit unterschiedlichen Klassen implementieren eine gemeinsame Schnittstelle immer noch alles stark Typ.C# verwendet viele Schnittstellen an vielen Orten.Meine Frage ist mehr: Warum ist th ist offensichtlich, eine fehlt? – Jens

+0

Wie Sie selbst geantwortet haben, können Sie mit Templates dasselbe für ähnliche Objekte tun. Wenn Math.Min so überladen ist, ist es nur zu vermeiden, Vorlagen zu verwenden und möglicherweise Mikro-Optimierung für spezielle Typen zu setzen. –

+2

Beachten Sie, dass C# keine Vorlagen enthält. Es hat Generika, was etwas anderes ist. –

4

, d. H. Wie 2 + 2.35 zu tun? zurückgeben 4 oder 4.35 oder 4.349999? Wie Interface verstehen, was ist die angeeignete Ausgabe? Sie können Ihre Erweiterungsmethode schreiben und Überladung verwenden, um dies für Ihr Problem zu lösen, aber wenn wir eine Schnittstelle für alle Zwecke haben möchten, wie lange wird die Schnittstellengröße sein und nützliche Funktionen finden, ist schwierig, auch eine Schnittstelle fügt einige Overhead und Zahlen sind in der Regel Basis Berechnung braucht also schnellen Weg.

Ich denke, schreiben eine Erweiterung Klasse in Ihrem Fall besser ist:

public static class ExtensionNumbers 
{ 
    public static T Range<T>(this T input, T min, T max) where T : class 
    { 
     return input.Max(input.Min(max), min); 
    } 

    public static T Min<T>(this T input, params T[] param) where T : class 
    { 
     return null; 
    } 

    private static T Max<T>(this T input, params T[] number) where T : class 
    { 
     return null; 
    }  

} 

I where T : class gerade zu sein

+1

muss ich nicht in meinem Verständnis. Siehe meine bearbeitete Frage. Es gibt entweder int oder double, aber nicht beides, oder? – Jens

+0

Ich denke, dass alle Operatoren für alle Nummern verfügbar sind, und das Hinzufügen einer Schnittstelle fügt nur einen gewissen Overhead hinzu, die Schnittstelle ist nützlich für die Verwaltung von Nummern, die in Entwicklergeist verwaltet werden, bevor Programmierung gelernt wird. –

+0

Ahh .. Ich sehe, was du meinst jetzt ... und Schnittstelle wie 'Öffentliche iNumber hinzufügen (iNumber a, iNumber b);' wäre in der Tat zu dem Problem führen Sie beschreiben ... lassen Sie mich darüber nachdenken =) – Jens

0

verwendet kompilieren Das Problem ist, dass in der Architektur, wie Zahlen gespeichert verschiedenen Nummerntypen sind unterschiedlich behandelt. Für Anfänger ist Ihr Wortschatz falsch und Interfaces funktionieren nicht, aber was ich denke ist, dass Sie wollen, dass Zahlen locker geschrieben werden.

Nur für einen Anfang, warum Sie dies nicht tun sollten Integer-Typen sind eine Eins-zu-Eins-Abbildung auf den Bereich der Werte, die sie darstellen können, während Fließkommatypen eine Persision und eine Exponentenkomponente haben Kleinkinder viele Gleitkommazahlen. Die Sprachentwickler müssten einige sehr grundlegende und potentiell fehlerverursachende Annahmen im Sprachdesign treffen.

Werfen Sie einen Blick auf diese article über Fließkomma-Mathe für weitere Informationen.

+2

Nein, ich will keine Nummern locker tippen. Das wäre schrecklich. =) Ich möchte einfach nicht meine 'Range' Methode schreiben (Bereich (x, min, max) = Max (Min (x, max), min)) für alle 16 numerischen Typen. – Jens

1

Gehen Sie mit Matthäus Antwort, bemerken Sie den Unterschied zwischen den 3 Anrufungen.

+0

Danke! Daran habe ich nie gedacht. Numerische Typen implementieren bereits eine Anzahl von Schnittstellen. Ist meine "vorgeschlagene" Anzahl anders als sie oder existiert dieses Problem heute? – Jens

1

Es ist, als die Einführung einer Schnittstelle ist nicht so einfach, da die verfügbaren Operatoren pro Typ verschieden sind, und sind nicht immer noch homogen (dh DateTime + TimeSpan => DateTime, DateTime - DateTime => TimeSpan).

Auf der technischen Ebene, gibt es viele Boxen usw. beteiligt, plus regelmäßige Betreiber sind static.

Eigentlich für Min/Max, Comparer<T>.Default.Compare(x,y) tut so ziemlich alles, was Sie hoffen würden.

Für andere Operatoren: in .NET 4.0 dynamic ist eine große Hilfe hier:

T x = ..., y = ...; 
T sum = (dynamic)x + (dynamic)y; 

aber ansonsten "MiscUtil" hat generic support for operators über die Operator Klasse, das heißt

T x = ..., y = ...; 
T sum = Operator.Add(x, y); // actually Add<T> 
Verwandte Themen