Warum erben alle Klassen in .NET global von der Object-Klasse?

16

Es ist sehr interessant für mich, welche Vorteile ein "global root class" -Ansatz für Framework bietet. In einfachen Worten , was Gründe ergaben , wurde der .NET - Framework entwickelt , um eine Wurzel hat Objektklasse mit dem allgemeinen Funktionalität geeignet für alle Klassen.

Heutzutage entwerfen wir ein neues Framework für den internen Gebrauch (das Framework unter SAP-Plattform) und teilen uns alle in zwei Lager auf - das erste, das der Meinung ist, dass das Framework eine globale Wurzel haben sollte, und das zweite, das der Meinung ist, dass das Gegenteil der Fall ist.

Ich bin im "Global Root" Camp. Und meine Gründe, warum ein solcher Ansatz zu einer guten Flexibilität und Reduzierung der Entwicklungskosten führen würde, sind, dass wir keine allgemeine Funktionalität mehr entwickeln werden.

Daher bin ich sehr daran interessiert, welche Gründe .NET-Architekten wirklich dazu bringen, Framework auf diese Weise zu entwerfen.

Anton Semenov
quelle
6
Denken Sie daran, dass .NET Framework Teil einer allgemeinen Softwareentwicklungsumgebung ist. Speziellere Frameworks wie Ihres benötigen möglicherweise kein "Root" -Objekt. Es gibt eine Wurzel objectim .NET-Framework, zum Teil, weil es einige grundlegende Funktionen für alle Objekte bereitstellt, wie z. B. ToString()undGetHashCode()
Robert Harvey
10
"Es ist sehr interessant zu wissen, aus welchen Gründen .NET-Architekten dazu veranlasst werden, Framework auf diese Weise zu entwerfen." Dafür gibt es drei gute Gründe: 1. Java hat es so gemacht, 2. Java hat es so gemacht und 3. Java hat es so gemacht.
dasblinkenlight
2
@vcsjones: und Java zum Beispiel bereits mit wait()/ notify()/ notifyAll()und verschmutzt clone().
Joachim Sauer
3
@daskblinkenlight Das ist poo. Java hat das nicht so gemacht.
Dante
2
@dasblinkenlight: Zu Ihrer Information: Java kopiert derzeit C # mit Lambdas, LINQ-Funktionen usw.
Mehrdad

Antworten:

18

Die dringlichste Ursache dafür Objectsind Container (vor Generika), die alles enthalten können, anstatt den C-Stil zu verwenden. Natürlich ist die Vorstellung, dass alles von einer bestimmten Klasse erben und diese Tatsache dann missbrauchen sollte, um jeden Punkt der Typensicherheit zu verlieren, so schrecklich, dass es ein riesiges Warnlicht für den Versand der Sprache ohne Generika gewesen sein sollte, und auch Mittel Das Objectist für neuen Code völlig überflüssig.

DeadMG
quelle
6
Das ist die richtige Antwort. Mangel an generischer Unterstützung war der einzige Grund. Andere "common-methods" -Argumente sind keine echten Argumente, da gemeinsame Methoden nicht unbedingt gemeinsam sein müssen - Beispiel: 1) ToString: In den meisten Fällen missbraucht; 2) GetHashCode: Je nachdem, was das Programm tut, benötigen die meisten Klassen diese Methode nicht. 3) GetType: muss keine Instanzmethode sein
Codism
2
Inwiefern verliert .NET "jeden Fleck Typensicherheit", indem es die Idee "missbraucht", dass alles von einer bestimmten Klasse geerbt werden soll? Gibt es eine shitcode , die um geschrieben werden können , Objectdass nicht geschrieben werden kann um void *in C ++?
Carson63000
3
Wer hat behauptet, ich wäre void*besser? Ist es nicht. Wenn überhaupt. es ist noch schlimmer .
DeadMG
void*ist in C mit allen impliziten Zeigermodellen schlechter, aber C ++ ist in dieser Hinsicht strenger. Zumindest muss man das explizit besetzen.
Tamás Szelei
2
@fish: Eine Auseinandersetzung mit der Terminologie: In C gibt es keine "implizite Besetzung". Eine Besetzung ist ein expliziter Operator, der eine Konvertierung angibt. Meinen Sie „impliziten Zeiger Conversions “.
Keith Thompson
11

Ich erinnere mich, dass Eric Lippert einmal sagte, dass das Erben aus der System.ObjectKlasse den besten Wert für den Kunden darstellte. [edit: yep, er hat es hier gesagt ]:

... Sie haben nicht brauchen einen gemeinsamen Basistyp. Diese Wahl wurde nicht aus Notwendigkeit getroffen. Es entstand aus dem Wunsch heraus, dem Kunden den bestmöglichen Wert zu bieten.

Wenn Sie ein Typensystem oder etwas anderes entwerfen, erreichen Sie manchmal Entscheidungspunkte - Sie müssen sich entweder für X oder für Nicht-X entscheiden ... Die Vorteile eines gemeinsamen Basistyps überwiegen die Kosten und der Nettonutzen davon Der aufgelaufene Betrag ist größer als der Nettovorteil, keinen gemeinsamen Basistyp zu haben. Daher wählen wir einen gemeinsamen Basistyp.

Das ist eine ziemlich vage Antwort. Wenn Sie eine spezifischere Antwort wünschen, stellen Sie eine spezifischere Frage.

Alles, was aus dem System.ObjectUnterricht stammt, bietet eine Zuverlässigkeit und Nützlichkeit, die ich sehr respektiere. Ich weiß, dass alle Objekte einen Typ ( GetType) haben werden, dass sie mit den Finalisierungsmethoden der CLR ( Finalize) übereinstimmen und dass ich GetHashCodesie beim Umgang mit Sammlungen verwenden kann.

gws2
quelle
2
Das ist fehlerhaft. Sie brauchen es nicht GetType, Sie können es einfach erweitern typeof, um seine Funktionalität zu ersetzen. Was Finalizedann tatsächlich sind viele Arten gar nicht vollständig finalizable und es eine sinnvolle Methode Finalize nicht besitzen. Darüber hinaus sind viele Typen weder hashbar noch konvertierbar in Zeichenfolgen - insbesondere interne Typen, die niemals für solche Zwecke vorgesehen sind und niemals für den öffentlichen Gebrauch ausgegeben werden. Bei all diesen Typen sind die Schnittstellen unnötig verschmutzt.
DeadMG
5
Nein, es ist nicht fehlerhaft - Ich habe nie behauptet , dass Sie verwenden würden GetType, Finalizeoder GetHashCodefür jeden System.Object. Ich habe nur gesagt, dass sie da sind, wenn Sie sie wollen. Bezüglich "ihrer unnötig verschmutzten Schnittstellen" verweise ich auf mein ursprüngliches Zitat von elippert: "bester Wert für den Kunden". Offensichtlich war das .NET-Team bereit, sich um die Kompromisse zu kümmern.
gws2
Wenn es nicht notwendig ist, dann ist es Grenzflächenverschmutzung. "Wenn Sie wollen" ist nie ein guter Grund, eine Methode aufzunehmen. Es sollte nur da sein, weil Sie es brauchen und Ihre Verbraucher es anrufen werden . Sonst ist es nicht gut. Außerdem ist Ihr Lippert-Zitat ein Appell an den Autoritätsfehler, da er auch nichts anderes sagt als "Es ist gut".
DeadMG
Ich versuche nicht, Ihre Argumente @DeadMG zu argumentieren - die Frage lautete speziell "Warum alle Klassen in .NET global von der Object-Klasse erben", und daher habe ich auf einen vorherigen Beitrag von jemandem verlinkt, der dem .NET-Team unter sehr nahe steht Microsoft, das eine berechtigte - wenn auch vage - Antwort gegeben hatte, warum sich das .NET-Entwicklungsteam für diesen Ansatz entschieden hatte.
gws2
Zu vage, um eine Antwort auf diese Frage zu geben.
DeadMG
4

Ich denke, dass Java- und C # -Designer ein Root-Objekt hinzugefügt haben, weil es für sie wie beim kostenlosen Mittagessen im Wesentlichen kostenlos war .

Die Kosten für das Hinzufügen eines Stammobjekts entsprechen weitgehend den Kosten für das Hinzufügen Ihrer ersten virtuellen Funktion. Da sowohl CLR als auch JVM Umgebungen mit Garbage Collected und Objekt-Finalizern sind, müssen Sie mindestens eine virtuelle Umgebung für Ihre java.lang.Object.finalizeoder Ihre System.Object.FinalizeMethode haben. Daher sind die Kosten für das Hinzufügen Ihres Stammobjekts bereits im Voraus bezahlt. Sie können alle Vorteile nutzen, ohne dafür zu bezahlen. Dies ist das Beste aus beiden Welten: Benutzer, die eine gemeinsame Root-Klasse benötigen, würden das bekommen, was sie wollen, und Benutzer, die es nicht weniger interessieren könnten, könnten so programmieren, als wäre sie nicht vorhanden.

dasblinkenlight
quelle
4

Anscheinend haben Sie hier drei Fragen: Zum einen wurde .NET mit einer gemeinsamen Wurzel versehen, zum anderen mit den Vor- und Nachteilen und zum anderen, ob ein globales Framework-Element eine gute Idee hat Wurzel.

Warum hat .NET eine gemeinsame Wurzel?

In dem am meisten technischen Sinne ist eine gemeinsame Wurzel, die wesentlich für die Reflexion und für Pre-Generika - Container.

Meines Wissens nach hatten sie eine gemeinsame Wurzel mit Basismethoden wie equals()und hashCode()wurden in Java sehr positiv aufgenommen, und C # wurde (unter anderem) von Java beeinflusst, so dass sie auch diese Funktion haben wollten.

Was sind die Vor- und Nachteile einer gemeinsamen Wurzel in einer Sprache?

Der Vorteil einer gemeinsamen Wurzel:

  • Sie können allen Objekten eine bestimmte Funktionalität zuweisen, die Sie für wichtig halten, z. B. equals()und hashCode(). Das bedeutet zum Beispiel, dass jedes Objekt in Java oder C # in einer Hash-Map verwendet werden kann - vergleichen Sie diese Situation mit C ++.
  • Sie können auf Objekte unbekannten Typs verweisen - z. B. wenn Sie nur Informationen übertragen, wie dies bei vorgenerischen Containern der Fall war.
  • Sie können sich auf Objekte mit unbekanntem Typ beziehen, z. B. bei Verwendung von Reflexion.
  • Es kann in den seltenen Fällen verwendet werden, wenn Sie ein Objekt akzeptieren möchten, das von einem anderen und ansonsten nicht verwandten Typ sein kann - z. B. als Parameter einer printfähnlichen Methode.
  • Es gibt Ihnen die Flexibilität, das Verhalten aller Objekte zu ändern, indem Sie nur eine Klasse ändern. Wenn Sie beispielsweise allen Objekten in Ihrem C # -Programm eine Methode hinzufügen möchten , können Sie eine Erweiterungsmethode hinzufügen object. Vielleicht nicht sehr verbreitet, aber ohne eine gemeinsame Wurzel wäre das nicht möglich.

Der Nachteil einer gemeinsamen Wurzel:

  • Es kann missbraucht werden, auf ein Objekt mit einem bestimmten Wert zu verweisen, selbst wenn Sie einen genaueren Typ dafür hätten verwenden können.

Sollten Sie in Ihrem Framework-Projekt eine gemeinsame Wurzel haben?

Sehr subjektiv natürlich, aber wenn ich mir die obige Pro-Contra-Liste anschaue, würde ich definitiv mit Ja antworten . Insbesondere der letzte Punkt in der Pro-Liste - die Flexibilität, das Verhalten aller Objekte später durch Ändern des Stamms zu ändern - ist in einer Plattform sehr nützlich, in der Änderungen am Stamm mit größerer Wahrscheinlichkeit von Clients akzeptiert werden als Änderungen am gesamten Stamm Sprache.

Außerdem finde ich - obwohl dies noch subjektiver ist - das Konzept einer gemeinsamen Wurzel sehr elegant und ansprechend. Dies erleichtert auch die Verwendung einiger Tools. Beispielsweise können Sie jetzt ein Tool so einstellen, dass es alle Nachkommen dieses gemeinsamen Stamms anzeigt und einen schnellen und guten Überblick über die Framework-Typen erhält.

Natürlich müssen die Typen zumindest ein wenig verwandt sein, und insbesondere würde ich niemals die "Ist-Eine" -Regel opfern, nur um eine gemeinsame Wurzel zu haben.

Eiche
quelle
Ich denke, c # wurde nicht nur von Java beeinflusst, es war irgendwann Java. Microsoft konnte Java nicht mehr verwenden und würde Milliarden an Investitionen verlieren. Sie begannen damit, der Sprache einen neuen Namen zu geben.
Mike Braun
3

Mathematisch gesehen ist es etwas eleganter, ein Typensystem zu haben, das top enthält und das es Ihnen ermöglicht, eine Sprache etwas vollständiger zu definieren.

Wenn Sie ein Framework erstellen, werden die Dinge notwendigerweise von einer vorhandenen Codebasis konsumiert. Sie können keinen universellen Supertyp haben, da alle anderen Typen, die das Framework verwenden, vorhanden sind.

An diesem Punkt hängt die Entscheidung, eine gemeinsame Basisklasse zu haben, davon ab, was Sie tun. Es kommt sehr selten vor, dass viele Dinge ein gemeinsames Verhalten haben und dass es nützlich ist, diese Dinge nur über das gemeinsame Verhalten zu referenzieren.

Aber es kommt vor. Wenn dies der Fall ist, abstrahieren Sie dieses gemeinsame Verhalten direkt.

Telastyn
quelle
2

Sie haben ein Root-Objekt gesucht, weil sie wollten, dass alle Klassen im Framework bestimmte Dinge unterstützen (einen Hash-Code abrufen, in einen String konvertieren, Gleichheitsprüfungen usw.). Sowohl in C # als auch in Java war es hilfreich, alle diese allgemeinen Funktionen eines Objekts in eine Root-Klasse einzufügen.

Denken Sie daran, dass sie keine OOP-Prinzipien oder Ähnliches verletzen. Alles in der Object-Klasse ist in jeder Unterklasse (sprich: in jeder Klasse) im System sinnvoll . Wenn Sie diesen Entwurf übernehmen möchten, achten Sie darauf, dass Sie diesem Muster folgen. Nehmen Sie also keine Dinge in den Stamm auf, die nicht zu jeder einzelnen Klasse in Ihrem System gehören. Wenn Sie diese Regel sorgfältig befolgen, sehe ich keinen Grund, warum Sie keine Root-Klasse haben sollten, die nützlichen, allgemeinen Code für das gesamte System enthält.

Oleksi
quelle
2
Das stimmt überhaupt nicht. Es gibt viele reine interne Klassen, die niemals gehasht, niemals reflektiert, niemals in einen String konvertiert werden. Unnötige Erbe und überflüssige Methoden definitiv tun viele Prinzipien verstoßen.
DeadMG
2

Eine Reihe von Gründen, die alle untergeordneten Klassen gemeinsam nutzen können. Und es gab den Framework-Autoren auch die Möglichkeit, viele andere Funktionen in das Framework zu schreiben, die alles nutzen konnte. Ein Beispiel wäre die ASP.NET-Zwischenspeicherung und -Sitzungen. In ihnen kann fast alles gespeichert werden, da sie die add-Methoden geschrieben haben, um Objekte zu akzeptieren.

Eine Root-Klasse kann sehr verführerisch sein, ist aber sehr, sehr leicht zu missbrauchen. Wäre es möglich, stattdessen ein Root-Interface zu haben? Und nur eine oder zwei kleine Methoden? Oder würde das viel mehr Code hinzufügen, als für Ihr Framework erforderlich ist?

Ich bin gespannt, welche Funktionen Sie für alle möglichen Klassen in dem Framework benötigen, das Sie schreiben. Wird das wirklich von allen Objekten genutzt? Oder von den meisten? Und wie können Sie verhindern, dass Benutzer nach dem Erstellen der Stammklasse zufällige Funktionen hinzufügen? Oder Zufallsvariablen, die sie "global" sein wollen?

Vor einigen Jahren gab es eine sehr große Anwendung, in der ich eine Stammklasse erstellt habe. Nach einigen Monaten der Entwicklung wurde es mit Code gefüllt, der nichts zu suchen hatte. Wir konvertierten eine alte ASP-Anwendung und die Root-Klasse war der Ersatz für die alte Datei global.inc, die wir in der Vergangenheit verwendet hatten. Musste diese Lektion auf die harte Tour lernen.

bwalk2895
quelle
2

Alle eigenständigen Heap-Objekte erben von Object; Dies ist sinnvoll, da alle eigenständigen Heap-Objekte bestimmte gemeinsame Aspekte aufweisen müssen, z. B. die Identifizierung ihres Typs. Wenn der Garbage-Collector einen Verweis auf ein Heap-Objekt eines unbekannten Typs hätte, hätte er keine Möglichkeit zu wissen, welche Bits in dem diesem Objekt zugeordneten Speicherblock als Verweise auf andere Heap-Objekte anzusehen sind.

Ferner ist es innerhalb des Typsystems zweckmäßig, den gleichen Mechanismus zum Definieren der Mitglieder von Strukturen und der Mitglieder von Klassen zu verwenden. Das Verhalten von Speicherorten vom Typ "Wert" (Variablen, Parameter, Felder, Array-Slots usw.) unterscheidet sich stark von dem von Speicherorten vom Typ "Klasse". Solche Verhaltensunterschiede treten jedoch bei den Quellcode-Compilern und der Ausführungs-Engine (einschließlich) auf JIT-Compiler), anstatt im Typsystem ausgedrückt zu werden.

Eine Konsequenz davon ist, dass das Definieren eines Werttyps effektiv zwei Typen definiert - einen Speicherorttyp und einen Heap-Objekttyp. Ersteres kann implizit in Letzteres konvertiert werden, und Letzteres kann durch Typumwandlung in Ersteres konvertiert werden. Beide Arten der Konvertierung funktionieren, indem alle öffentlichen und privaten Felder von einer Instanz des betreffenden Typs in eine andere kopiert werden. Darüber hinaus ist es mit generischen Einschränkungen möglich, Schnittstellenelemente an einem Speicherort vom Werttyp direkt aufzurufen, ohne zuvor eine Kopie davon zu erstellen.

All dies ist wichtig, da Verweise auf Heap-Objekte vom Typ Wert sich wie Klassenverweise und nicht wie Werttypen verhalten. Betrachten Sie beispielsweise den folgenden Code:

string testEnumerator <T> (T it) wobei T: IEnumerator <string>
{
  var it2 = it;
  it.MoveNext ();
  it2.MoveNext ();
  return it.Current;
}
öffentlicher ungültigkeitstest ()
{
  var theList = new List <string> ();
  theList.Add ("Fred");
  theList.Add ("George");
  theList.Add ("Percy");
  theList.Add ("Molly");
  theList.Add ("Ron");

  var enum1 = theList.GetEnumerator ();
  IEnumerator <string> enum2 = enum1;

  Debug.Print (testEnumerator (enum1));
  Debug.Print (testEnumerator (enum1));
  Debug.Print (testEnumerator (enum2));
  Debug.Print (testEnumerator (enum2));
}

Wenn der testEnumerator()Methode ein Speicherort vom Werttyp übergeben wird, itwird eine Instanz empfangen, deren öffentliche und private Felder aus dem übergebenen Wert kopiert werden. Die lokale Variable enthält it2eine andere Instanz, von der alle Felder kopiert wurden it. Der Aufruf MoveNextauf it2keinen Einfluss auf it.

Wenn der obige Code an einen Speicherort des Klassentyps übergeben wird, beziehen sich die übergebenen Werte itund it2alle auf dasselbe Objekt, und wenn MoveNext()einer von ihnen aufgerufen wird, wird dies effektiv für alle von ihnen aufgerufen.

Beachten Sie, dass Casting, List<String>.Enumeratorum es IEnumerator<String>effektiv von einem Werttyp in einen Klassentyp umzuwandeln. Der Typ des Heap-Objekts ist, List<String>.Enumeratoraber sein Verhalten unterscheidet sich stark vom gleichnamigen Werttyp.

Superkatze
quelle
+1 für ToString () nicht erwähnen
JeffO
1
Hier geht es um Implementierungsdetails. Es ist nicht erforderlich, diese dem Benutzer zur Verfügung zu stellen.
DeadMG
@DeadMG: Was meinst du? Das beobachtbare Verhalten von a, List<T>.Enumeratordas in a gespeichert List<T>.Enumeratorist, unterscheidet sich erheblich von dem Verhalten von a, das in a gespeichert IEnumerator<T>ist, indem das Speichern eines Werts des vorherigen Typs an einem Speicherort eines der beiden Typen eine Kopie des Enumeratorstatus erstellt. Beim Speichern eines Wertes des letzteren Typs an einem Speicherort, der ebenfalls vom letzteren Typ ist, wird keine Kopie erstellt.
Supercat
Ja, allerdings Objecthat nichts mit dieser Tatsache zu tun.
DeadMG
@DeadMG: Mein Punkt ist, dass eine Heap-Instanz des Typs System.Int32auch eine Instanz von ist System.Object, aber ein Speicherort des Typs System.Int32weder eine System.ObjectInstanz noch eine Referenz auf eine enthält.
Supercat
2

Dieses Design geht wirklich auf Smalltalk zurück, das ich größtenteils als Versuch ansehen würde, die Objektorientierung auf Kosten fast aller anderen Bedenken zu verfolgen. Als solches tendiert es (meiner Meinung nach) dazu, Objektorientierung zu verwenden, selbst wenn andere Techniken wahrscheinlich (oder sogar sicher) überlegen sind.

Wenn Sie eine einzelne Hierarchie mit Object(oder etwas Ähnlichem) im Stammverzeichnis haben, ist es (zum Beispiel) ziemlich einfach, Ihre Auflistungsklassen als Auflistungen von zu erstellen. Daher ist Objectes für eine Auflistung trivial, Objekte jeder Art zu enthalten.

Als Gegenleistung für diesen eher geringen Vorteil erhalten Sie jedoch eine ganze Reihe von Nachteilen. Aus Sicht des Designs haben Sie zunächst einige wirklich verrückte Ideen. Was haben Atheismus und ein Wald nach der Java-Sicht des Universums gemeinsam? Dass beide Hashcodes haben! Ist eine Karte eine Sammlung? Laut Java ist das nicht der Fall!

In den 70er Jahren, als Smalltalk entworfen wurde, wurde diese Art von Unsinn akzeptiert, vor allem, weil niemand eine vernünftige Alternative entworfen hatte. Smalltalk wurde 1980 fertiggestellt und 1983 wurde Ada (einschließlich Generika) entwickelt. Obwohl Ada nie die von manchen vorhergesagte Popularität erlangte, reichten seine Generika aus, um Sammlungen von Objekten beliebigen Typs zu unterstützen - ohne den Wahnsinn, der den monolithischen Hierarchien innewohnt.

Bei der Entwicklung von Java (und in geringerem Maße von .NET) wurde die monolithische Klassenhierarchie wahrscheinlich als "sichere" Wahl angesehen - eine mit Problemen, aber meist bekannten Problemen. Im Gegensatz dazu war die generische Programmierung eine, die fast jeder (schon damals) als eine theoretisch viel bessere Herangehensweise an das Problem erkannte , die jedoch von vielen kommerziell orientierten Entwicklern als wenig erforscht und / oder riskant angesehen wurde (dh in der Geschäftswelt) (Ada wurde weitgehend als Misserfolg abgetan).

Lassen Sie mich jedoch klar und deutlich sein: Die monolithische Hierarchie war ein Fehler. Die Gründe für diesen Fehler waren zumindest verständlich, aber es war trotzdem ein Fehler. Es ist ein schlechtes Design und seine Designprobleme durchdringen fast den gesamten Code, der es verwendet.

Für ein neues Design gibt es heute jedoch keine vernünftige Frage: Die Verwendung einer monolithischen Hierarchie ist ein klarer Fehler und eine schlechte Idee.

Jerry Sarg
quelle
Es ist erwähnenswert, dass Vorlagen bekanntermaßen großartig und nützlich waren, bevor .NET ausgeliefert wurde.
DeadMG
In der kommerziellen Welt, Ada war ein Fehler, nicht so sehr wegen seiner Ausführlichkeit, aber aufgrund des Mangels an standardisierten Schnittstellen zu Betriebssystemdienste. Keine Standard-API für das Dateisystem, die Grafik, das Netzwerk usw. machte die Entwicklung von "gehosteten" Anwendungen durch Ada extrem teuer.
Kevin Cline
@kevincline: Ich hätte das wahrscheinlich etwas anders formulieren sollen - dahingehend, dass Ada als Ganzes aufgrund seines Marktversagens als Versager abgetan wurde. Die Ablehnung der Verwendung von Ada war (IMO) durchaus vernünftig - aber die Ablehnung, daraus zu lernen (bestenfalls).
Jerry Coffin
@ Jerry: Ich denke, dass C ++ - Vorlagen die Ideen von Ada-Generika übernahmen und sie dann durch implizite Instanziierung erheblich verbesserten.
Kevin Cline
1

Was Sie tun möchten, ist tatsächlich interessant. Es ist schwer zu beweisen, ob es falsch oder richtig ist, bis Sie fertig sind.

Hier sind einige Dinge zum Nachdenken:

  • Wann würden Sie dieses übergeordnete Objekt jemals absichtlich über ein spezifischeres Objekt übergeben?
  • Welchen Code möchten Sie in jede einzelne Ihrer Klassen einfügen?

Dies sind die einzigen zwei Dinge, die von einer gemeinsamen Wurzel profitieren können. In einer Sprache werden einige sehr gebräuchliche Methoden definiert, mit denen Sie Objekte übergeben können, die noch nicht definiert wurden, die jedoch für Sie nicht gelten sollten.

Auch aus eigener Erfahrung:

Ich habe ein Toolkit verwendet, das einmal von einem Entwickler mit Smalltalk-Hintergrund geschrieben wurde. Er machte es so, dass alle seine "Data" -Klassen eine einzelne Klasse erweiterten und alle Methoden diese Klasse beanspruchten - soweit so gut. Das Problem ist, dass die verschiedenen Datenklassen nicht immer austauschbar waren. Mein Editor und mein Compiler konnten mir also KEINE Hilfestellung geben, was in eine bestimmte Situation zu übergehen ist, und ich musste auf seine Dokumente verweisen, die nicht immer hilfreich waren. .

Das war die schwierigste Bibliothek, mit der ich jemals umgehen musste.

Bill K
quelle
0

Weil das der Weg der OOP ist. Es ist wichtig, einen Typ (Objekt) zu haben, der auf alles verweisen kann. Ein Argument vom Typ Objekt kann alles annehmen. Es gibt auch Vererbung, Sie können sicher sein, dass ToString (), GetHashCode () usw. für alles und jeden verfügbar sind.

Sogar Oracle hat die Bedeutung eines solchen Basistyps erkannt und plant, Primitive in Java ab 2017 zu entfernen

Dante
quelle
6
Es ist weder der Weg der OOP noch wichtig. Sie geben keine tatsächlichen Argumente außer "Es ist gut".
DeadMG
1
@DeadMG Sie können die Argumente nach dem ersten Satz lesen. Grundelemente verhalten sich anders als Objekte und zwingen den Programmierer, viele Varianten desselben Zweckcodes für Objekte und Grundelemente zu schreiben.
Dante
2
@DeadMG Nun, er sagte, es bedeutet, dass Sie davon ausgehen können ToString()und GetType()auf jedem Objekt sein werden. Es ist wahrscheinlich erwähnenswert, dass Sie eine Variable vom Typ verwenden können object, um ein beliebiges .NET-Objekt zu speichern.
JohnL
Es ist auch durchaus möglich, einen benutzerdefinierten Typ zu schreiben, der eine Referenz eines beliebigen Typs enthalten kann. Sie benötigen keine speziellen Sprachfunktionen, um dies zu erreichen.
DeadMG
Sie sollten ein Objekt nur auf Ebenen haben, die bestimmten Code enthalten. Sie sollten niemals nur ein Objekt haben, um Ihr Diagramm zusammenzufügen. Das "Objekt" implementiert tatsächlich einige wichtige Methoden und fungiert als eine Möglichkeit, ein Objekt an Werkzeuge zu übergeben, bei denen der Typ noch nicht einmal erstellt wurde.
Bill K