Wie kann System.String für die Groß- / Kleinschreibung korrekt umgebrochen werden?

8

Diese Frage ist nicht zum Verwalten von Windows-Pfadnamen; Ich habe das nur als spezifisches Beispiel für eine Zeichenfolge ohne Beachtung der Groß- und Kleinschreibung verwendet. (Und wenn ich jetzt das Beispiel ändere, wird eine ganze Reihe von Kommentaren bedeutungslos sein.)

Dies kann ähnlich sein Mögliche Groß- und Kleinschreibung String-Klasse erstellen? , aber dort gibt es nicht viel Diskussion. Außerdem interessiert mich nicht die enge Sprachintegration, die string genießt, oder die Leistungsoptimierung von System.String .

Lassen Sie sich sagen, dass ich eine Menge von Windows-Pfadnamen verwenden, die (in der Regel) Groß- und Kleinschreibung (ich eigentlich nicht mit den vielen Details der tatsächlichen Pfade betroffen wie \ vs. / , \\ ist die gleiche als \ , file:// URLs, .. usw.). Ein einfacher Wrapper könnte sein:

%Vor%

Ja, alle / die meisten Schnittstellen auf System.String sollten wahrscheinlich implementiert sein; aber das obige scheint für Diskussionszwecke genug zu sein.

Ich kann jetzt schreiben:

%Vor%

Dadurch kann ich in meinem Code über " WindowsPathname s" sprechen und nicht über ein Implementierungsdetail wie StringComparison.OrdinalIgnoreCase . (Ja, das spezifisch Klasse auch erweitert werden könnte, um Griff \ vs / , so dass c: /foo.txt wäre gleich C: \ foo.txt , aber das ist nicht der Sinn dieser Frage) Darüber hinaus ist diese Klasse (mit zusätzlichen Schnittstellen) wird Groß- und Kleinschreibung, wenn Instanzen Sammlungen hinzugefügt. Es wäre nicht notwendig, ein IEqualityComparer anzugeben. Schließlich auch eine spezielle Klasse wie das macht es einfacher „non-sense“ Operationen zu verhindern, wie einen Dateisystempfad zu einem Registrierungsschlüssel verglichen wird.

Die Frage ist: Wird ein solcher Ansatz erfolgreich sein? Gibt es irgendwelche ernsthaften und / oder subtilen Mängel oder andere „Fallstricke“? (Wieder mit mit dem Versuch, das Einrichten eines Groß- und Kleinschreibung String-Klasse zu tun, nicht Windows-Pfadnamen zu verwalten.)

    
Ðаn 09.10.2015, 13:16
quelle

5 Antworten

8

Ich würde eine unveränderliche Struktur erstellen, die eine Zeichenkette enthält, die die Zeichenkette im Konstruktor in einen Standardfall (z. B. Kleinbuchstaben) umwandelt. Dann könnten Sie auch den impliziten Operator hinzufügen, um die Erstellung zu vereinfachen und die Vergleichsoperatoren zu überschreiben. Ich denke, das ist der einfachste Weg, um das Verhalten zu erreichen, und Sie erhalten nur einen kleinen Overhead (die Konvertierung ist nur im Konstruktor).

Hier ist der Code:

%Vor%

Hier ist die Verwendung:

%Vor%

Dies funktioniert auch für Sammlungen.

    
AleFranz 13.10.2015, 00:37
quelle
3

Sie möchten also ein Objekt, das eine Zeichenfolge in ein Objekt konvertiert, und wenn Sie zwei Zeichenfolgen in zwei dieser Objekte konvertieren, möchten Sie diese Objekte auf Gleichheit mit Ihren eigenen Regeln über die Gleichheit der Objekte vergleichen können zwei Objekte.

In Ihrem Beispiel geht es um Groß- und Kleinschreibung, aber es könnte auch um Vorwärtsstriche und Rückwärtsstriche gehen, vielleicht möchten Sie sogar definieren, dass das "Wort" USD gleich $ ist.

Angenommen, Sie teilen die Auflistung aller möglichen Zeichenfolgen in Untersammlungen von Zeichenfolgen auf, die Sie als gleich definieren. In diesem Fall wäre "Hello" in der gleichen Subcollection wie "HELLO" und "hElLO". Vielleicht wäre "c: \ temp" in der gleichen Sammlung wie "c: / TEMP".

Wenn Sie etwas finden könnten, um Ihre Untersammlung zu identifizieren, könnten Sie sagen, dass alle Zeichenfolgen, die zu derselben Untersammlung gehören, denselben Bezeichner hätten. Oder mit anderen Worten: Alle Zeichenfolgen, die Sie als gleich definiert haben, würden denselben Bezeichner haben.

Wenn das möglich wäre, würde es ausreichen, den Subcollection Identifier zu vergleichen. Wenn zwei Strings denselben Untersammlungsbezeichner haben, gehören sie zu derselben Untersammlung und werden daher gemäß unserer Gleichheitsdefinition als gleich betrachtet.

Wir nennen diesen Bezeichner den normalisierten Wert der Zeichenfolge . Der Konstruktor von CaseInsensitiveString könnte die Eingabezeichenfolge in den normalisierten Wert der Zeichenfolge konvertieren. Um zwei Objekte auf Gleichheit zu prüfen, müssen wir nur prüfen, ob sie den gleichen normalisierten Wert haben.

Ein Beispiel für die Normalisierung einer Zeichenfolge wäre:

  • Machen Sie die Zeichenfolge in Kleinbuchstaben
  • mache alle Schrägstriche rückwärts Schrägstriche
  • Konvertiere alle Wörter USD in $
  • Entferne alle tausend Trennzeichen in Zahlen ohne Tausendertrennzeichen
  • usw., abhängig davon, wann die Strings gleich sein sollen.

Nach dem obigen würden die folgenden Strings alle zur selben normalisierten Zeichenkette führen:

  • Weißes Haus $ 1.000.000
  • Weißes Haus $ 1000000
  • weißes Haus USD 1000000

Wir können alles als eine normalisierte Zeichenkette definieren, solange alle Zeichenketten, die wir gleich definieren, die gleiche normalisierte Zeichenkette haben. Ein gutes Beispiel wäre

  • weißes Haus $ 1000000

Hinweis: Ich gehe nicht ins Detail, um Wörter wie USD und Tausendertrennzeichen zu finden. Die Bedeutung ist, dass Sie die Bedeutung der normalisierten Zeichenfolge verstehen.

Nachdem dies gesagt wurde, ist der einzige schwierige Teil, den stringIdentifier zu finden. Der Rest der Klasse ist ziemlich einfach:

Code für die Konstruktion. Der Konstruktor übernimmt einen String und bestimmt die Subcollection, zu der er gehört. Ich habe auch einen Standardkonstruktor hinzugefügt.

%Vor%

Gleichheit: Per Definition sind zwei Objekte identisch, wenn sie den gleichen normalisierten Wert haben

Siehe MSDN zum Definieren der Wertgleichheit für einen Typ

%Vor%

Beachten Sie, dass diese letzte Zeile der einzige Code ist, für den wir tatsächlich die Gleichheitsprüfung durchführen!

Alle anderen Gleichheitsfunktionen verwenden nur die oben definierte Funktion Equals:

%Vor%

Nun können Sie Folgendes tun:

%Vor%

Jetzt müssen wir nur noch die Normalize-Funktion implementieren. Sobald Sie wissen, wann zwei Strings gleich sind, wissen Sie, wie Sie normalisieren.

Angenommen, zwei Strings sind gleich, wenn Groß- / Kleinschreibung nicht berücksichtigt wird, und Schrägstriche sind die gleichen wie Rückwärtsstriche. (schlechtes Englisch)

Wenn die normalize-Funktion die gleiche Zeichenfolge in Kleinbuchstaben mit allen umgekehrten Schrägstrichen zurückgibt, dann haben zwei Zeichenfolgen, die wir für gleich halten, den gleichen normalisierten Wert

%Vor%     
Harald Coppoolse 13.10.2015 12:44
quelle
0

Ein kürzerer und leichterer Ansatz könnte darin bestehen, eine Erweiterungsmethode zu erstellen:

%Vor%

Dies erfordert weit weniger Programmierung als das Erstellen einer ganzen separaten Klasse, hat keinen Leistungsaufwand (kann sogar inline sein), keine zusätzlichen Zuweisungen und drückt auch die Absicht ziemlich deutlich aus IMO:

%Vor%     
Groo 09.10.2015 22:52
quelle
-1

Hmm ... Ich denke nicht, dass String Case die einzige Herausforderung ist, die Sie haben. Lassen Sie mich Ihnen ein paar Fragen stellen:

Ist c:\myPath gleich c:/myPath ? Wie wäre es mit file:////c:/myPath ? Oder wie wäre es mit \myMachine\c$\myPath ?

Ich verstehe, wo Sie hinwollen und was Sie erreichen wollen, aber es scheint, als wären Sie bei einem einfachen Problem mit dem Tunnel konfrontiert - warum sollten Sie ein Framework erstellen, das einen einfachen .ToLower() vs ToLower() Vergleich macht geht das?

Wenn Ihr Problembereich, zusätzlich zur String-Hülle , beinhaltet, die absolute Gleichheit zweier gegebener Pfade zu bewerten, ist es sinnvoll, eine Klasse zu schreiben. Aber das würde eine viel kompliziertere Lösung erfordern als das, was Sie vorschlagen ...

HTH!

    
code4life 09.10.2015 17:03
quelle
-1

Zuerst Einen Spaten als Spaten
aufrufen Sie müssen definieren, was die eindeutige Verantwortlichkeit der Klasse ist.

Entweder möchten Sie, dass die Klasse Windows-Pfadnamen verwaltet, und Sie können nicht alle diesbezüglichen Anmerkungen verwerfen, da der "Codeverwaltungsfall" mit den "Codeverwaltungspfaden" zusammengeführt wird. Die Kopplung macht es dann unmöglich, das Gehäuse ohne Berücksichtigung des Pfades zu testen (und das richtige Verhalten sicherzustellen).

Oder Sie möchten einen CaseInvariantString implementieren und ihn dann entsprechend benennen (und ihn möglicherweise in einer anderen Klasse namens WindowsPathname verwenden).

Für Referenzen über Klassenverhalten, Zusammenhalt, Kopplung und andere großartige Konzepte, würde ich die folgenden Bücher empfehlen:

  • Clean Code von Robert C. Martin (Onkel Bob)
  • Code Complete von Steeve McConnell

Zweitens kann das Umbrechen von Zeichenfolgen innerhalb einer Klasse zur Überprüfung der Fallinvarianz als umschließende Ganzzahl in einer PositivInteger-Klasse betrachtet werden. Es kann (und wird) von manchen als übertrieben angesehen werden . Es ist eine gemeinsame Tendenz aller Entwickler, den Gipfel des objektorientierten Dogmas zu erreichen. Hier scheint es so zu sein, alle Werttypen in der Klasse zu verpacken (wie int in eine ID-Klasse). Vergessen Sie jedoch nicht, Ihnen Fragen zu stellen.

  • Was kostet die Übernahme solcher Praktiken?
  • Was sind die Vorteile?
  • Zu welchen Schwierigkeiten kann es führen?
  • Könnte ich den allgemeinen Ansatz für alle meine Projekte übernehmen?
  • Habe ich eine Verabredung mit meinem technischen Leiter / Architekten (oder einer ähnlichen Behörde), dass es eine gute Praxis ist?

Schließlich, als ein einfacher technischer Punkt. Sie sollten in Ihrer Klasse keine Zeichenfolge erstellen . Es ist schädlich für die Leistung. Da Zeichenketten invariant sind, wird in der Tat ein ToUpperInvariant() in GetHashCode() erstellt und ein neues String erstellt.

Und aus Gründen der Pfadinvarianz ... Es funktioniert nicht außerhalb von Windows
( Für Mono, offensichtlich / foo! = / Foo ).

    
Fab 17.10.2015 16:26
quelle

Tags und Links