Warum ist Array nicht ein generischer Typ?
Array
ist erklärt wie die folgenden:
public abstract class Array
: ICloneable, IList, ICollection, IEnumerable {
Frage ich mich, warum ist es nicht:
public partial class Array<T>
: ICloneable, IList<T>, ICollection<T>, IEnumerable<T> {
-
Was wäre das Problem, wenn es wurde erklärt, ein generischer Typ?
-
Wenn es einen generischen Typ, wir müssen noch die nicht-Generika? Und wenn wir würde, könnte es nur ableiten aus
Array<T>
(wie oben angenommen)?public partial class Array: Array<object> {
update:
Der zweite Punkt ist auch wichtig, um die Annahme von nicht mehr machen, die nicht generisch Array
ein basic-Typ.
InformationsquelleAutor der Frage Ken Kin | 2013-01-14
Du musst angemeldet sein, um einen Kommentar abzugeben.
Geschichte
Zurück in C# 1.0 Sie kopierten das Konzept von arrays, die hauptsächlich aus Java. Generika gab es damals noch nicht, aber die Macher dachten, Sie waren schlau und kopiert die gebrochen kovariante array-Semantik von Java-arrays haben. Dies bedeutet, dass Sie abziehen können Dinge wie diese, ohne einen compile-Zeit-Fehler (aber eine Laufzeit-Fehlermeldung statt):
In C# 2.0 generics eingeführt wurden, aber keine kovariante/kontravariant generische Typen. Wenn arrays gemacht wurden generische, dann könnte Sie nicht wirken
Mammoth[]
zuAnimal[]
etwas, das Sie tun konnte, bevor (auch wenn es defekt war). Somit können arrays generische würde gebrochen haben viel code.Nur in C# 4.0 wurden kovariante/kontravariant generische Typen für Schnittstellen eingeführt. Dies machte es möglich, zu beheben die gebrochenen array-Kovarianz-einmal und für alle. Aber auch dies würde gebrochen haben viel bestehenden code.
Arrays implementieren generischer interfaces
Dank einer runtime-trick jedes array
T[]
hat implementierenIEnumerable<T>
ICollection<T>
undIList<T>
automatisch.1 Von derArray
- Klasse Dokumentation:Nicht. Die Dokumentation setzt sich mit dieser Bemerkung:
Weil (zum Beispiel)
ICollection<T>
hat eineAdd
Methode, aber Sie können nicht hinzufügen, alles zu einem array. Es wird eine exception werfen. Dies ist ein weiteres Beispiel für einen frühen design-Fehler in der .NET-Framework mit der man Ausnahmen, die bei Ihnen bei der Laufzeit:Und da
ICollection<T>
ist nicht kovariante (aus offensichtlichen Gründen), Sie kann das nicht tun:Natürlich gibt es nun die kovariante
IReadOnlyCollection<T>
interface , ist auch die Implementierung von arrays unter der Haube1aber es enthält nurCount
so hat es begrenzte Anwendungen.Der Basis-Klasse
Array
In den frühen Tagen haben wir. Alle arrays implementieren die nicht-generische
IList
ICollection
undIEnumerable
Schnittstellen durch Ihre Basis-KlasseArray
. Dies war die einzige vernünftige Möglichkeit zu geben, alle arrays von spezifischen Methoden und Schnittstellen, und ist der primäre nutzen derArray
Basisklasse. Sie sehen, die gleiche Wahl für enums: Sie sind value-Typen Erben, die Mitglieder vonEnum
; und die Delegierten, dass Erben vonMulticastDelegate
.Ja, der Methoden und Schnittstellen, die gemeinsam von allen arrays definiert werden kann, auf die generischen
Array<T>
Klasse, wenn es überhaupt zustande kam. Und dann könnte man schreiben, zum BeispielCopy<T>(T[] source, T[] destination)
stattCopy(Array source, Array destination)
mit dem zusätzlichen Vorteil, eine gewisse Art der Sicherheit.Jedoch von einem Objekt-Orientierte Programmier-Sicht ist es schön, einen gemeinsamen nicht-generische Basisklasse
Array
verwendet werden können, finden Sie alle array unabhängig von der Art Ihrer Elemente. So wieIEnumerable<T>
erbt vonIEnumerable
(die ist noch in einige LINQ-Methoden).Nein, das würde eine zyklische Abhängigkeit erzeugen:
Array<T> : Array : Array<object> : Array : ...
. Auch, dass würde bedeuten, Sie könnte Shop alle - Objekt in ein array (nachdem alle, alle arrays, die letztlich den Erben von TypArray<object>
).Die Zukunft
Nicht. Während die syntax könnte gemacht werden, um zu passen, das vorhandene array-Kovarianz-konnte nicht verwendet werden.
Ein array ist ein spezieller Typ .NET. Es hat sogar seinen eigenen Anweisungen in die Common Intermediate Language. Wenn die .NET-und C# - Designer jemals entscheiden, zu gehen dieser Weg, Sie könnten machen die
T[]
syntax syntaktische Zucker fürArray<T>
(so wieT?
ist syntaktischer Zucker fürNullable<T>
), und noch verwenden Sie die spezielle Anleitung und Unterstützung, der die Zuweisung der arrays im Speicher zusammenhängend.Jedoch, Sie würden die Fähigkeit verlieren, cast-arrays
Mammoth[]
zu einem der BasistypenAnimal[]
ähnlich wie die, die du nicht wirken kannstList<Mammoth>
zuList<Animal>
. Aber array Kovarianz ist trotzdem abgebrochen, und es gibt bessere alternativen.Alle arrays implementieren
IList<T>
. Wenn dieIList<T>
- Schnittstelle vorgenommen wurden, in eine richtige kovariante Schnittstelle, dann könnte man die Darsteller alle array -Array<Mammoth>
(oder jede Liste für diese Angelegenheit) zu einerIList<Animal>
. Dies erfordert jedoch, dass dieIList<T>
- Schnittstelle umgeschrieben werden entfernen Sie alle Methoden, die möglicherweise Auswirkungen auf das zugrunde liegende array:(Beachten Sie, dass die Typen der Parameter, input-Positionen nicht
T
wie dieser brechen würde Kovarianz. Allerdingsobject
ist gut genug fürContains
undIndexOf
wer würde nur zurückfalse
wenn übergeben ein Objekt einen falschen Typ. Und Sammlungen implementieren dieser Schnittstellen können eigene generischeIndexOf(T value)
undContains(T value)
.)Dann könnte man dies tun:
Gibt es sogar eine kleine Leistungssteigerung, da die Laufzeit würde nicht haben, um zu überprüfen, ob eine zugewiesene Wert-Typ kompatibel mit den tatsächlichen Typ der array-Elemente bei der Einstellung der Wert ein element eines Arrays.
Meinen stab an es
Ich nahm einen Stich an, wie so eine
Array<T>
Art funktionieren würde, wäre es in C# implementiert und .NETZ, kombiniert mit der realen kovarianteIList<T>
undICollection<T>
- Schnittstellen, die oben beschrieben und es funktioniert ganz gut. Ich habe auch die invarianteIMutableList<T>
undIMutableCollection<T>
Schnittstellen zur Bereitstellung der mutation Methoden, die meine neueIList<T>
undICollection<T>
Schnittstellen fehlen.Baute ich eine einfache Sammlung Bibliothek um ihn herum, und Sie können laden Sie den Quellcode und die kompilierte binaries von BitBucketoder installieren Sie das NuGet-Paket:
1) Ein array
T[]
.Net 4.5, implementiert durch die in der BasisklasseArray
:ICloneable
IList
ICollection
IEnumerable
IStructuralComparable
IStructuralEquatable
; und lautlos durch die Laufzeit:IList<T>
ICollection<T>
IEnumerable<T>
IReadOnlyList<T>
undIReadOnlyCollection<T>
.InformationsquelleAutor der Antwort Virtlink
Kompatibilität. Array ist eine historische Art, die geht zurück auf die Zeit, dass es keine Generika.
Heute würde es Sinn machen, haben
Array
dannArray<T>
dann die gewünschte Klasse 😉InformationsquelleAutor der Antwort TomTom
[Update, neue Erkenntnisse, es fühlte, dass etwas fehlte bis jetzt]
Bezug auf die frühere Antwort:
Aber der andere Grund, warum ich denken kann, ist da ein array die 'Basis-Typ' für eine lineare Reihe von Elementen im Speicher. Ich habe darüber nachgedacht, mit Array<T>, die ist, wo Sie vielleicht auch Fragen, warum T ist ein Objekt, und warum sich dieses "Objekt" überhaupt existiert? In diesem Szenario T[] nur, was ich als eine andere syntax für Array<T> die kovariante mit Array. Da die Typen auch tatsächlich unterscheiden, halte ich das in beiden Fällen ähnlich.
Beachten Sie, dass sowohl ein basic-Objekt und ein basic-Arrays sind nicht Voraussetzung für eine OO Sprache. C++ ist das perfekte Beispiel dafür. Der Nachteil der nicht mit einem basic-Typ für diese grundlegenden Konstrukte nicht in der Lage, die Arbeit mit arrays oder Objekte mit der spiegelung. Für Objekte, die Sie verwendet, um zu machen, Foo Dinge, die macht ein 'Objekt' fühlen sich natürlich an. In der Realität, nicht mit einem array-Basis-Klasse macht es ebenso unmöglich, das zu tun Foo -- die nicht so Häufig verwendet werden, aber ebenso wichtig für das Paradigma.
Daher, dass C# ohne eine Array-base-Typ, aber mit dem Reichtum-runtime-Typen (insbesondere der reflektion), ist IMO unmöglich.
Also mehr in die details...
Wo sind arrays verwendet und warum sind Sie arrays
Dass eine einfache Art für etwas so grundlegend wie ein array verwendet wird, für eine Menge Dinge, und das mit gutem Grund:
Na ja, wir wussten bereits, dass Menschen
T[]
wie Sie es verwendenList<T>
. Beide implementieren einen gemeinsamen Satz von Schnittstellen, um genau zu sein:IList<T>
ICollection<T>
IEnumerable<T>
IList
ICollection
undIEnumerable
.Können Sie ganz einfach ein Array erstellen, wenn Sie wissen das. Wir alle wissen auch, dass dies wahr ist, und es ist nicht aufregend, so bewegen wir uns auf...
Wenn Sie Graben sich in die Liste am Ende hast du ein Array schließlich - um genau zu sein: ein T[] array.
So, warum ist das? Während Sie haben könnte verwendet einen Zeiger-Struktur (LinkedList), es ist einfach nicht das gleiche. Listen sind kontinuierliche Blöcke von Speicher und Holen Sie sich Ihre Geschwindigkeit, indem Sie einen kontinuierlichen Speicherblock. Es gibt eine Menge von Gründen, aber einfach ausgedrückt: Verarbeitung von endlos-Speicher ist die Schnellste Art der Verarbeitung-Speicher - es gibt auch Anweisungen für die, die in Ihrer CPU, die schneller ist.
Einem aufmerksamen Leser verweist auf die Tatsache, dass Sie brauchen nicht ein array, sondern einen kontinuierlichen block von Elementen des Typs 'T' , IL versteht und verarbeiten kann. In anderen Worten, Sie könnte loswerden die Array-Typ, hier, so lange wie Sie sicherstellen, dass es gibt eine andere Art, die verwendet werden können, die von IL, das gleiche zu tun.
Beachten Sie, dass es Wert und Klasse Typen. Um weiterhin die bestmögliche Leistung, die Sie benötigen, speichern Sie Sie in Ihrem block, wie-wie... aber für den Lotsen ist es einfach eine Voraussetzung.
Marshalling verwendet grundlegende Typen, alle Sprachen zu vereinbaren, um zu kommunizieren. Diese Grundtypen sind Dinge wie byte, int, float, Zeiger... und array. Vor allem ist der Weg arrays sind in C/C++, das ist so:
Grundsätzlich das setzt einen Zeiger auf den Beginn des array und inkrementiert den Zeiger (mit sizeof(Foo) bytes), bis er das Ende des Arrays. Das element wird abgerufen *foo - was bekommt das element des pointer 'foo' gerichtet ist.
Beachten Sie wieder, dass es Werttypen und Referenztypen. Sie wirklich nicht wollen, eine MyArray, die einfach speichert alles boxed als ein Objekt. Die Umsetzung MyArray gerade eine Hölle von viel mehr tricky.
Etwas sorgfältiger Leser kann auf die Tatsache, dass Sie hier nicht wirklich brauchen, ein array hier, das ist wahr. Sie brauchen eine kontinuierliche-block-Elemente mit dem Typ Foo - und wenn es einen Wert geben, es muss gespeichert werden, in den block, wie die (byte-Darstellung) Wert Typ.
So mehr... und Was ist mit multi-Dimensionalität? Anscheinend die Regeln sind nicht so schwarz und weiß, weil wir plötzlich nicht mehr alle base-Klassen mehr:
Starker Typ ging einfach aus dem Fenster, und Sie am Ende mit collection-Typen
IList
ICollection
undIEnumerable
. Hey, wie sollen wir die Größe dann? Bei Verwendung der Array-Basis-Klasse, konnten wir verwendet haben:... aber wenn wir uns die alternativen wie
IList
gibt es keine Entsprechung. Wie lösen wir diese? Sollte die Einführung einerIList<int, int>
hier? Dies ist sicherlich falsch, weil das basic-Typ ist nurint
. WasIMultiDimentionalList<int>
? Wir können das nicht, und füllen Sie ihn mit den Methoden, die derzeit im Array.Haben Sie bemerkt, dass es spezielle Aufrufe für die Neuzuweisung von arrays? Das hat alles zu tun mit memory-management: arrays sind so low-level, dass Sie nicht verstehen, was Wachstum oder Schrumpfung sind. In C würden Sie 'malloc' und 'realloc' für diese, und Sie wirklich umsetzen sollten, Ihre eigenen 'malloc' und 'realloc' zu verstehen, warum genau mit festen Größen ist wichtig für alle Dinge, die Sie direkt zuweisen.
Wenn man es betrachtet, es gibt nur ein paar Dinge, die uns zugeteilt, die in einer "festen" Größen: arrays, alle grundlegenden Datentypen, Zeigern und Klassen. Offenbar haben wir handle-arrays unterschiedlich, genau wie wir behandeln grundlegende Typen unterschiedlich.
Einen Seite ein Hinweis über die Art der Sicherheit
Warum also müssen diese alle diese 'access point' Schnittstellen in den ersten Platz?
Die besten Praktiken in allen Fällen ist es, den Nutzern eine sichere Typ-point-of-access. Dies kann illustriert durch den Vergleich von code wie dieser:
code wie dieser:
Geben Sicherheit damit Sie schlampig bei der Programmierung. Wenn richtig eingesetzt, wird der compiler den Fehler zu finden, wenn Sie, statt zu finden, die out-run-Zeit. Ich kann nicht genug betonen, wie wichtig dies ist - nach allem, wird Ihr code kann nicht aufgerufen werden in einem test-Fall zu sein, während der compiler immer es zu bewerten!
Setzen Sie alle zusammen
Also... sagen wir es alle zusammen. Wir wollen:
Das ist schon ein bisschen low-level-Anforderungen für jede Sammlung... es erfordert Speicher organisiert werden, in einer bestimmten Art und Weise sowie Umstellung auf IL/CPU... ich würde sagen, es ist ein guter Grund, es gilt als eine grundlegende Art.
InformationsquelleAutor der Antwort atlaste
Der Grund ist, dass Generika nicht vorhanden waren in der ersten version von C#.
Das problem ist, dass es brechen würde, eine riesige Menge von code, der verwendet die
Array
Klasse. C# unterstützt keine mehrfache Vererbung, also Zeilen wie diesewäre gebrochen.
Wenn MS damit wurden C# - und .NET alles immer wieder von vorne an, dann gäbe es wohl kein problem im
Array
eine generische Klasse, aber das ist nicht die Realität.InformationsquelleAutor der Antwort JLRishe
Ist wie jeder sagt - original
Array
ist nicht-generisch, weil es keine Generika, wenn es kam in die Existenz in v1. Spekulationen unter...Machen "Array" generische (was würde nun Sinn machen), können Sie entweder
bleiben die bestehenden
Array
und fügen Sie generische version. Das ist schön, aber die meisten Verwendungen von "Array" einzubeziehen, es wächst im Laufe der Zeit und ist es wahrscheinlich, dass eine bessere Umsetzung des gleichen KonzeptsList<T>
umgesetzt wurde, statt. An dieser Stelle hinzufügen von generischen version von "sequenzielle Liste von Elementen, die nicht wachsen" sieht nicht sehr Ansprechend.entfernen Sie nicht generic
Array
und ersetzen Sie durch generischeArray<T>
Umsetzung mit der gleichen Schnittstelle. Jetzt haben Sie, um kompilierten code für ältere Versionen der Arbeit mit dem neuen Typ anstelle der bestehendenArray
geben. Zwar wäre es möglich (auch wahrscheinlich schwer) für framework-code zu unterstützen, wie migration, es gibt immer eine Menge code geschrieben von anderen Menschen.Als
Array
ist sehr basic Typ so ziemlich jedes Stück von bestehenden code (enthält benutzerdefinierte code mit Reflexion und marshalling mit native code und COM) verwendet. Als Ergebnis der Preis noch so kleinen Inkompatibilität zwischen den Versionen (1.x -> 2.x von .Net Framework) sehr hoch wären.Also als Ergebnis
Array
Art ist es für immer bleiben. Wir haben jetztList<T>
als generische äquivalent verwendet werden.InformationsquelleAutor der Antwort Alexei Levenkov
Zusätzlich zu den anderen Probleme, die Leute erwähnt haben, versuchen, fügen Sie ein generisches
Array<T>
darstellen würde, ein paar andere Schwierigkeiten:Auch wenn heute die Kovarianz-Funktionen gab es schon von dem moment an, generics eingeführt wurden, Sie wäre nicht ausreichend gewesen, für arrays. Eine routine, die entworfen, um eine Art
Car[]
wird in der Lage sein, zu Sortieren, eineBuick[]
sein, auch wenn es zu kopieren Elemente aus dem array in das Elemente vom TypCar
und dann kopieren Sie Sie zurück. Kopieren Sie das element vom TypCar
zurück zu einemBuick[]
ist nicht wirklich type-safe ist, aber es ist nützlich. Man könnte definieren, die eine kovariante array eindimensionales array-Schnittstelle in einer Weise zu gestalten, damit die Sortierung möglich [z.B. durch eine " Swap(int firstIndex, int secondIndex) - Methode], aber es wäre schwierig, etwas zu machen, was so flexibel wie arrays sind.Während ein
Array<T>
geben die gut funktionieren könnte für einT[]
würde, gäbe es keine Mittel innerhalb der generische Typ system definieren, eine Familie, die umfassen würdeT[]
T[,]
T[,,]
T[,,,]
usw. für eine beliebige Anzahl von Indizes.Gibt es kein Mittel .net express die Vorstellung, dass die beiden Typen sollten identisch betrachtet werden, so dass eine variable vom Typ
T1
kopiert werden kann, um eine ArtT2
und Umgekehrt, mit beiden Variablen hält Referenzen auf das gleiche Objekt. Jemanden mit einemArray<T>
geben würde wahrscheinlich wollen, um passieren zu können Instanzen von code erwartetT[]
und akzeptieren Sie die Instanzen von code verwendetT[]
. Wenn old-style-arrays konnte nicht übergeben werden, und vom code mit dem neuen Stil, dann die new-style-arrays wäre mehr ein Hindernis als ein feature.Möglicherweise gibt es Möglichkeiten, jinxing das Typ-system zu ermöglichen, eine Art
Array<T>
dass verhielt sich wie es sollte, aber so ein Typ wäre, Verhalten sich in vielerlei Hinsicht, die völlig Verschieden von anderen generischen Typen, und da gibt es schon eine geben, die sich um das gewünschte Verhalten (d.h.T[]
), es ist nicht klar, was für Vorteile erwachsen aus der Definition der anderen.InformationsquelleAutor der Antwort supercat
Vielleicht bin ich etwas fehlt, aber es sei denn, der array-Instanz, angeheuert oder verwendet als eine ICollection, IEnumerable, etc.. dann musst du nicht alles gewinnen, mit einer Reihe von T.
Arrays sind schnell und sind bereits typsicher ist und nicht entstehen boxing/unboxing overhead.
InformationsquelleAutor der Antwort user1758003