Unveränderbare Datenstrukturen in C #

8

Ich habe ein paar Einträge in Eric Lipperts Blog über unveränderliche Datenstrukturen gelesen und bin dabei zu denken, warum hat C # dies nicht in die Standardbibliotheken eingebaut? Es scheint komisch, dass etwas mit offensichtlicher Wiederverwendung nicht bereits out of the box implementiert ist.

BEARBEITEN: Ich denke, dass ich bei meiner Frage missverstanden werden könnte. Ich frage nicht, wie man eine in C # implementiert, ich frage, warum einige der grundlegenden Datenstrukturen (Stack, Warteschlange, etc.) nicht bereits als unveränderliche Varianten verfügbar sind.

    
doctorless 14.12.2011, 18:37
quelle

6 Antworten

2

Ich zitiere von Eric Lippert blog , die Sie gelesen haben:

  

weil niemand dieses Feature entworfen, spezifiziert, implementiert, getestet, dokumentiert und ausgeliefert hat.

Mit anderen Worten, es gibt keinen anderen Grund als den, dass es nicht hoch genug war, um vor allen anderen Dingen, an denen sie arbeiten, erledigt zu werden.

    
Scott Pedersen 14.12.2011, 19:17
quelle
6

Es tut jetzt.

.NET hat gerade seine erste unveränderliche Sammlungen , die ich schlage vor, Sie auszuprobieren.

    
Andrew Arnott 24.12.2012 16:41
quelle
3

Jeder Rahmen, jede Sprache oder Kombination davon, der keine rein experimentelle Übung ist, hat einen Markt. Einige rein experimentelle entwickeln einen Markt.

In diesem Sinne bezieht sich "Markt" nicht unbedingt auf die Marktwirtschaft, es ist ebenso wahr, ob die Produzenten des Frameworks / der Sprache / beide kommerziell oder nicht-kommerziell orientiert sind und den Rahmen / die Sprache / beide verteilen (ich bin werde jetzt einfach "Framework" sagen, kostenlos oder kostenlos. In der Tat können frei-in-Bier-und-Rede-Projekte sogar noch stärker von ihren Märkten abhängig sein als kommerzielle Projekte, weil ihre Produzenten eine Untergruppe ihres Marktes darstellen. Der Markt ist jeder und jeder der es benutzt.

Die Art dieses Marktes wird den Rahmen in mehrfacher Hinsicht beeinflussen, sowohl durch organische Prozesse (einige Teile werden beliebter als andere und erhalten einen größeren Anteil des Mindspace innerhalb der Gemeinschaft, die ihre eigenen Mitglieder über sie erzieht) und durch Fiat ( jemand entscheidet, dass ein Feature dem Markt dient und fügt es daher hinzu).

Als .NET entwickelt wurde, wurde es entwickelt, um seinen zukünftigen Markt zu bedienen. Ideen darüber, was ihnen dienen würde, beeinflussten daher die Entscheidungen darüber, was in der FCL, in der Laufzeit und in den damit zusammenhängenden Sprachen enthalten sein sollte und was nicht.

Jemand hat entschieden, dass wir sehr wahrscheinlich System.Collections.ArrayList benötigen. Jemand hat entschieden, dass wir sehr wahrscheinlich System.IO.DirectoryInfo benötigen, um eine Delete() -Methode zu haben. Niemand hat entschieden, dass wir wahrscheinlich eine System.Collections.ImmutableStack brauchen würden.

Vielleicht hat niemand darüber nachgedacht. Vielleicht hat es jemand getan und sogar umgesetzt, und dann wurde entschieden, dass es nicht genug allgemein verwendet werden sollte. Vielleicht wurde es ausführlich in Microsoft debattiert. Ich habe nie für MS gearbeitet, also habe ich keine Ahnung.

Was ich jedoch in Betracht ziehen kann, ist die Frage, was die Leute, die 2002 das .NET-Framework benutzten, im Jahr 2001 benutzten.

Nun, COM, ActiveX, ("Classic") ASP und VB6 & amp; VBScript wird jetzt viel weniger verwendet als es war, daher kann gesagt werden, dass es durch .NET ersetzt wurde. In der Tat könnte man das als Absicht bezeichnen.

Sowie VB6 & amp; VBScript, eine beträchtliche Anzahl, die in C ++ und Java mit Windows als einziger oder primärer Zielplattform geschrieben haben, verwenden nun zumindest teilweise .NET. Ich denke, das könnte man wiederum als Absicht bezeichnen, oder zumindest glaube ich nicht, dass MS überrascht wäre, dass es so gelaufen ist.

In COM hatten wir einen enumerator-objektbasierten foreach-Ansatz zur Iteration, der direkte Unterstützung in einigen Sprachen (VB-Familie *) hatte, und .NET haben einen auf Enumerator-Objekt basierenden foreach-Ansatz zur Iteration, der in einigen direkt unterstützt wird Sprachen (C #, VB.NET und andere) †.

In C ++ hatten wir einen reichen Satz von Sammlungsarten aus der STL, und in .NET haben wir eine reichhaltige Sammlung von Sammlertypen aus der FCL (und typsicheren generischen Typen ab .NET2.0).

In Java hatten wir einen starken Alles-ist-ein-Objekt-Stil von OOP mit einer kleinen Reihe von Methoden, die von einem gemeinsamen Basistyp und einem Boxmechanismus bereitgestellt wurden, um einfache effiziente Primitive zu ermöglichen, während dieser Stil beibehalten wurde. In .NET haben wir einen starken Alles-ist-ein-Objekt-Stil von OOP mit einer kleinen Menge von Methoden, die durch einen gemeinsamen Basistyp und einen (anderen) Boxmechanismus bereitgestellt werden, um einfache effiziente Primitive zu ermöglichen, während dieser Stil beibehalten wird / p>

Diese Fälle zeigen Entscheidungen, die nicht überraschend sind, wenn man bedenkt, wer wahrscheinlich der Markt für .NET sein wird (obwohl solche allgemeinen Aussagen nicht so gelesen werden sollten, dass die Arbeit und die Subtilität der Probleme in jedem der Projekte unterschätzt werden Sie). Eine andere Sache, die damit zusammenhängt, ist, wenn sich .NET von COM oder klassischem VB oder C ++ oder Java unterscheidet, kann es in der Dokumentation ein wenig Erklärung geben. Wenn .NET sich von Haskell oder Lisp unterscheidet, fühlt sich niemand verpflichtet, darauf hinzuweisen!

Nun, natürlich gibt es Dinge, die in .NET anders als in einem der oben genannten Dinge gemacht werden (oder es hätte keinen Sinn gemacht und wir hätten bei COM usw. bleiben können.)

Mein Punkt ist jedoch, dass aus dem nahezu unendlichen Bereich von möglichen Dingen, die in einem Framework wie .NET enden könnten, ein paar vollkommene No-Brainer existieren ("sie brauchen vielleicht eine Art von String-Typ ..."). "), einige naheliegende (" das ist wirklich einfach in COM zu tun, so muss es leicht in .NET zu tun sein "), einige härtere Aufrufe (" das wird komplizierter sein als in VB6, aber die Vorteile sind es wert "), einige Verbesserungen (" Gebietsschema-Unterstützung könnte wirklich viel einfacher für Entwickler gemacht werden, so können wir einen neuen Ansatz für das alte Problem erstellen ") und einige, die weniger mit dem oben genannten verbunden waren.

Auf der anderen Seite können wir uns wahrscheinlich alle etwas vorstellen, das so bizarr ist ("Hey, alle Programmierer mögen Conways Leben - setzen wir Conways Leben in den Rahmen") und daher gibt es keine Überraschung wenn es nicht unterstützt wird.

Bis jetzt habe ich schnell eine Menge harter Arbeit und schwierige Designbalancen in einer Weise geputzt, die das Design, das sie hervorgebracht haben, einfacher erscheinen lässt, als es ohne Zweifel war. Je "offensichtlicher" es einem Außenseiter nach der Tat erscheint, desto schwieriger ist es für die Designer.

Unveränderliche Sammlungstypen fallen in die große Auswahl möglicher Komponenten der FCL, die zwar nicht so bizarr wie eine eingebaute Conway-Support-Idee war, aber nicht so stark gefordert wurde, indem der Markt als veränderbare Liste oder Weg untersucht wurde Gebietsschema Informationen gut einzukapseln. Es wäre zu einem großen Teil des anfänglichen Marktes neu gewesen und daher dem Risiko ausgesetzt gewesen, nicht verwendet zu werden. In einem alternativen Universum gibt es ein .NET1 mit unveränderbaren Sammlungen, aber es ist nicht sehr überraschend, dass es hier kein einziges gab.

* Zumindest für den Konsum. Das Produzieren von IEnumVARIANT Implementierungen in VB6 war nicht einfach und könnte das Schreiben von Zeigerwerten direkt in v-Tabellen auf eine ziemlich unangenehme Weise beinhalten, die mir plötzlich einfällt, ist möglicherweise nicht einmal durch die heutige DEP erlaubt.

† Mit einer manchmal unmöglich zu implementierenden Methode .Reset() . Gibt es dafür einen anderen Grund als in IEnumVARIANT ? Wurde es jemals in IEnumVARIANT verwendet?

    
Jon Hanna 16.12.2011 18:18
quelle
2

Warum können Sie keine unveränderliche Struktur erstellen? Berücksichtigen Sie Folgendes:

%Vor%

Es ist ein unveränderlicher Werttyp.

    
Trevor Elliott 14.12.2011 18:41
quelle
2

Es ist schwierig, mit unveränderlichen Datenstrukturen zu arbeiten, es sei denn, Sie haben einige funktionale Programmierkonstrukte. Angenommen, Sie möchten einen unveränderlichen Vektor erstellen, der jeden anderen Großbuchstaben enthält. Wie würdest du es tun, außer du

A) hatte Funktionen wie range(65, 91) , filter(only even) und map(int -> char) , um die Sequenz auf einmal zu erstellen und dann in ein Array umzuwandeln

B) erzeugte den Vektor als veränderlich, fügte die Zeichen in einer Schleife hinzu, "fror" sie dann und machte sie unveränderlich?

Übrigens, C # hat in gewissem Umfang die Option B - ReadOnlyCollection kann eine veränderbare Auflistung umbrechen und verhindern, dass Benutzer sie mutieren. Es ist jedoch ein Schmerz in den Arsch, dies die ganze Zeit zu tun (und offensichtlich ist es schwierig, die Struktur zwischen Kopien zu teilen, wenn man nicht weiß, ob etwas unveränderlich wird oder nicht.) A ist eine bessere Option. p>

Denken Sie daran, als C # 1.0 existierte, hatte es keine anonymen Funktionen, es hatte keine Sprachunterstützung für Generatoren oder andere Faulheit, es hatte keine funktionalen APIs wie LINQ - nicht einmal Karte oder Filter - es hatte keine präzise Array-Initialisierungssyntax (Sie konnten new int[] { 1, 2, 5 } nicht schreiben) und es gab keine generischen Typen; einfach Dinge reinzulegen und Zeug aus Sammlungen zu holen war normalerweise ein Schmerz. Ich denke also nicht, dass es eine gute Wahl gewesen wäre, Zeit damit zu verbringen, robuste, unveränderbare Sammlungen mit so wenig Sprachunterstützung für deren Verwendung zu erstellen.

    
mquander 14.12.2011 19:08
quelle
0

Es wäre schön, wenn .net wirklich solide Unterstützung für unveränderliche Dateninhaber (Klassen und Strukturen) hätte. Eine Schwierigkeit bei der Hinzufügung von wirklich guter Unterstützung für solche Dinge besteht jedoch darin, dass der größtmögliche Vorteil veränderbarer und unveränderlicher Datenstrukturen einige fundamentale Änderungen an der Art, wie Vererbung funktioniert, erfordert. Während ich diese Unterstützung im nächsten großen objektorientierten Framework sehen möchte, weiß ich nicht, dass es effizient in bestehende Frameworks wie .net oder Java integriert werden kann.

Um das Problem zu sehen, stellen Sie sich vor, dass es zwei grundlegende Datentypen gibt: basicItem und deluxeItem (was ein basicItem mit einigen zusätzlichen Feldern ist). Jeder kann in zwei konkreten Formen existieren: veränderlich und unveränderlich. Jeder kann auch in abstrakter Form beschrieben werden: lesbar. Daher sollte es sechs Datentypen geben; Alle außer ReadableBasicItem sollten für mindestens eine andere Option ersetzbar sein:

  1. ReadableBasicItem: Für nichts austauschbar
  2. MutableBasicItem: ReadableBasicItem
  3. ImmutableBasicItem: ReadableBasicItem
  4. ReadableDeluxeItem: ReadableBasicItem
  5. MutableDeluxeItem: ReadableDeluxeItem, MutableBasicItem (auch ReadableBasicItem)
  6. ImmutableDeluxeItem: ReadableDeluxeItem, ImmutableBasicItem (auch ReadableBasicItem)

Selbst wenn der zugrunde liegende Datentyp nur eine Basis und einen abgeleiteten Typ hat, hat das Vererbungsdiagramm zwei "Rauten", da sowohl "MutableDeluxeItem" als auch "ImmutableDeluxeItem" zwei Eltern haben (MutableBasicItem und ReadableDeluxeItem), die beide von ReadableBasicItem erben . Bestehende Klassenarchitekturen können damit nicht effektiv umgehen. Beachten Sie, dass es nicht erforderlich ist, generalisierte Mehrfachvererbung zu unterstützen. nur um einige spezifische Varianten wie die oben genannten zu ermöglichen (die, obwohl sie im Vererbungsgraphen "Diamanten" haben, eine interne Struktur haben, so dass sowohl ReadableDeluxeItem als auch MutableBasicItem von "demselben" ReadableBasicItem erben).

Auch wenn die Unterstützung für diesen Vererbungsstil veränderbarer und unveränderlicher Typen nett sein könnte, würde die größte Belohnung nicht eintreten, wenn das System nicht imstande wäre, im Heap gespeicherte Objekte zu unterscheiden, die Wertesemantiken von denen preisgeben sollten, die sie aufdecken sollten Referenzsemantik, könnte veränderbare Objekte von unveränderlichen unterscheiden und könnte Objekten ermöglichen, in einem "nicht festgeschriebenen" Zustand (weder änderbar noch garantiert unveränderlich) zu beginnen. Das Kopieren eines Verweises auf ein Heap-Objekt mit Semantik mit veränderbarem Wert sollte einen Memberly-Klon für dieses Objekt und alle verschachtelten Objekte mit Semantik für veränderbare Werte durchführen, außer in Fällen, in denen die ursprüngliche Referenz garantiert zerstört werden würde; Die Klone sollten das Leben als nicht festgeschrieben starten, aber CompareExchange sollte nach Bedarf veränderlich oder unveränderlich sein.

Das Hinzufügen von Framework-Unterstützung für solche Features würde ermöglichen, dass die Semantik des Kopierens und Schreibens viel effizienter implementiert wird, als dies ohne Framework-Unterstützung möglich wäre, aber eine solche Unterstützung müsste wirklich von Grund auf in das Framework eingebaut werden. Ich denke nicht, dass es sich sehr gut auf einen bestehenden Rahmen überlagern könnte.

    
supercat 15.12.2011 16:25
quelle

Tags und Links