wake-up-neo.com

Implizite Konvertierung von char in eine einzelne Zeichenfolge

Zuallererst: Ich weiß, wie man dieses Problem umgeht. Ich suche keine Lösung. Ich interessiere mich für die Gründe für die Designentscheidungen, die zu einigen impliziten Konvertierungen und nicht zu anderen führten.

Heute bin ich auf einen kleinen, aber einflussreichen Fehler in unserer Codebasis gestoßen, bei dem eine int-Konstante mit einer char-Darstellung derselben Nummer initialisiert wurde. Dies führt zu einer ASCII Umwandlung der char in eine int. Etwas wie das:

char a = 'a';
int z = a;
Console.WriteLine(z);    
// Result: 97

Ich war verwirrt, warum C # so etwas zuließ. Nachdem ich mich umgesehen hatte, fand ich die folgende SO Frage mit einer Antwort von Eric Lippert selbst: Implizite Typumwandlung in C #

Ein Ausschnitt:

Wir können jedoch fundierte Vermutungen anstellen, warum implizites Char-to-Ushort als gute Idee angesehen wurde. Die Schlüsselidee dabei ist, dass die Umwandlung von Zahl in Zeichen eine "möglicherweise zweifelhafte" Umwandlung ist. Es geht darum, etwas zu nehmen, von dem Sie nicht wissen, dass es ein Charakter ist, und es als einen zu behandeln. Das scheint die Art von Dingen zu sein, die Sie explizit aufrufen möchten, anstatt es versehentlich zuzulassen. Aber das Gegenteil ist viel weniger zweifelhaft. In der C-Programmierung hat es eine lange Tradition, Zeichen als Ganzzahlen zu behandeln - um ihre zugrunde liegenden Werte zu erhalten oder um Mathematik mit ihnen zu üben.

Ich kann der Argumentation dahinter zustimmen, obwohl ein IDE -Hinweis großartig wäre. Ich habe jedoch eine andere Situation, in der die implizite Konvertierung plötzlich nicht legal ist:

char a = 'a';
string z = a; // CS0029 Cannot implicitly convert type 'char' to 'string'

Diese Umstellung ist meiner bescheidenen Meinung nach sehr logisch. Es kann nicht zu Datenverlust führen und die Absicht des Autors ist auch sehr klar. Auch nachdem ich den Rest der Antwort zur impliziten Konvertierung von char in int gelesen habe, sehe ich immer noch keinen Grund, warum dies nicht legal sein sollte.

Das führt mich zu meiner eigentlichen Frage:

Welche Gründe könnte das C # -Designteam haben, die implizite Konvertierung von char in string nicht zu implementieren, obwohl dies so offensichtlich erscheint (insbesondere beim Vergleich mit der Konvertierung von char in int).

9
pyrocumulus

Zunächst einmal, wie ich immer sage, wenn jemand fragt: "Warum nicht?" Frage zu C #: Das Designteam muss keinen Grund angeben, um notdo a feature. Features kosten Zeit, Aufwand und Geld, und jedes Feature, das Sie tun, kostet Zeit, Aufwand und Geld von betterEigenschaften.

Aber ich möchte die Prämisse nicht ohne weiteres ablehnen. Die Frage könnte besser formuliert werden als "Was sind die Vor- und Nachteile dieses vorgeschlagenen Features für das Design?"

Dies ist eine durchaus vernünftige Funktion, und es gibt Sprachen, mit denen Sie einzelne Zeichen als Zeichenfolgen behandeln können. (Tim hat VB in einem Kommentar erwähnt, und Python behandelt Zeichen und Strings mit einem Zeichen auch als austauschbare IIRC. Ich bin mir sicher, dass es noch andere gibt.) Ich möchte jedoch darauf hinweisen, dass ich das Feature geworfen habe ein paar Nachteile:

  • Dies ist eine neue Form der Boxkonvertierung. Zeichen sind billige Werttypen. Zeichenfolgen sind Heap-zugewiesene Referenztypen. Boxing-Konvertierungen können Leistungsprobleme verursachen und zu Druck auf die Sammlung führen. Daher ist zu begründen, dass sie im Programm sichtbarer und nicht weniger sichtbar sein sollten.
  • Die Funktion wird nicht als "Zeichen können in Zeichenfolgen mit einem Zeichen umgewandelt werden" angesehen. Es wird von Benutzern als "chars areone-character strings" wahrgenommen, und jetzt ist es völlig vernünftig, viele knock-on-Fragen zu stellen, wie: Kann .Length für ein Zeichen aufrufen? Wenn ich ein Zeichen übergeben kann an eine Methode, die eine string erwartet, und ich kann einen String an eine Methode übergeben, die einen IEnumerable<char> erwartet, kann ich eine char an eine Methode übergeben, die einen IEnumerable<char> erwartet? Das scheint ... seltsam. Ich kann Select und Where auf einem aufrufen string; kann ich auf einem char? Das scheint noch seltsamer zu sein. Das einzige vorgeschlagene Feature ist verschiebe deine Frage; Wäre es implementiert worden, würden Sie jetzt fragen: "Warum kann ich Select auf einem char nicht anrufen? "oder so etwas.

  • Kombinieren Sie nun die beiden vorhergehenden Punkte. Wenn ich Zeichen als Zeichenfolgen mit einem Zeichen betrachte und ein Zeichen in ein Objekt umwandle, erhalte ich dann ein Zeichen in einer Box oder eine Zeichenfolge?

  • Wir können den zweiten Punkt auch etwas weiter verallgemeinern. Eine Zeichenfolge ist eine Sammlung von Zeichen. Wenn wir sagen wollen, dass ein Zeichen in eine Sammlung von Zeichen konvertierbar ist, warum dann mit Zeichenfolgen aufhören? Warum nicht auch sagen, dass ein char auch als List<char> verwendet werden kann? Warum mit char aufhören? Sollen wir sagen, dass ein int in IEnumerable<int> konvertierbar ist?
  • Wir können noch weiter verallgemeinern: Wenn es eine offensichtliche Umwandlung von char in eine Folge von Zeichen in einem String gibt, dann gibt es auch eine offensichtliche Umwandlung von char in Task<char> - erstelle einfach eine abgeschlossene Aufgabe, die das Zeichen zurückgibt - und zu Func<char> - erstelle einfach ein Lambda, das das Zeichen zurückgibt - und zu Lazy<char> und zu Nullable<char> - oh, warte, wir tunerlauben eine Konvertierung zu Nullable<char>. :-)

Alle diese Probleme sind lösbar, und einige Sprachen haben sie gelöst. Darum geht es nicht. Das Problem ist: Alle diese Probleme sind Probleme, die das Sprachdesign-Team identifizieren, diskutieren und lösen muss. Eines der grundlegenden Probleme beim Sprachdesign ist wie allgemein sollte diese Funktion sein? In zwei Minuten I Wurde von "Zeichen sind in Einzelzeichenfolgen konvertierbar" auf "Jeder Wert eines zugrunde liegenden Typs ist in einen äquivalenten Wert eines monadischen Typs konvertierbar" umgestellt. Für beide Features und für verschiedene andere Punkte muss ein Argument angeführt werden auf das Spektrum der Allgemeinheit. Wenn Sie Ihre Sprachmerkmale zu spezifisch machen, wird es eine Masse von Sonderfällen, die schlecht miteinander interagieren. Wenn Sie sie zu allgemein machen, naja, ich denke, Sie haben Haskell. :-)

Angenommen, das Designteam kommt zu einer Schlussfolgerung in Bezug auf das Merkmal: All dies muss in den Designdokumenten und der Spezifikation sowie im Code niedergeschrieben werden, und Tests müssen geschrieben werden. Oh, habe ich das jedes Mal erwähnt? Ändern Sie die Konvertierbarkeitsregeln, wenn der Code für die Überladungsauflösung einer Person kaputt geht. Konvertierbarkeitsregeln müssen in der ersten Version wirklich stimmen, da eine spätere Änderung den vorhandenen Code anfälliger macht. Es gibt echte Designkosten und echte Benutzer, wenn Sie diese Art von Änderung in Version 8 anstelle von Version 1 vornehmen.

Vergleichen Sie nun diese Nachteile - und ich bin sicher, dass es weitere gibt, die ich nicht aufgelistet habe - mit den Vorteilen. Die Vorteile sind ziemlich klein: Sie vermeiden einen einzelnen Aufruf von ToString oder + "" oder was auch immer Sie tun, um ein Zeichen explizit in einen String zu konvertieren.

Dies ist noch lange nicht ausreichend, um die Kosten für Design, Implementierung, Test und Abwärtskompatibilität zu rechtfertigen.

Wie ich sagte, es ist eine vernünftige Funktion, und wenn es in Version 1 der Sprache gewesen wäre - die keine Generika oder eine installierte Basis von Milliarden von Codezeilen hatte - dann wäre es viel einfacher gewesen, es zu verkaufen. Aber jetzt gibt es viele Funktionen, die mehr für weniger Geld bieten.

10
Eric Lippert

Char ist ein Werttyp. Nein, es ist nicht numerisch wie int oder double, aber es leitet sich trotzdem von System.ValueType ab. Dann lebt eine char Variable auf dem Stapel.

String ist ein Referenztyp. Also lebt es auf dem Haufen. Wenn Sie einen Werttyp als Referenztyp verwenden möchten, müssen Sie ihn zuerst boxen. Der Compiler muss sicher sein, dass Sie über das Boxen wissen, was Sie tun. Sie können also keine implizite Besetzung haben.

2
SylF

Wenn Sie eine char in int umwandeln, ändern Sie nicht nur ihren Typ, sondern erhalten ihren Index in der ASCII -Tabelle, was eine ganz andere Sache ist um "ein zeichen in ein int umzuwandeln", geben sie ihnen nützliche daten für einen möglichen bedarf.

Warum nicht die Besetzung einer char zu string lassen?

Denn sie repräsentieren tatsächlich ganz unterschiedliche Dinge und werden auf ganz unterschiedliche Weise gespeichert.

Ein Zeichen und eine Zeichenfolge sind keine zwei Möglichkeiten, dasselbe auszudrücken.

0
Marco Salerno