Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
.NET bietet umfassende Unterstützung für die Entwicklung lokalisierter und globalisierter Anwendungen und erleichtert das Anwenden der Konventionen der aktuellen Kultur oder einer bestimmten Kultur beim Ausführen allgemeiner Vorgänge wie Sortieren und Anzeigen von Zeichenfolgen. Das Sortieren oder Vergleichen von Zeichenfolgen ist jedoch nicht immer ein kulturabhängiger Vorgang. Beispielsweise sollten Zeichenfolgen, die intern von einer Anwendung verwendet werden, in der Regel in allen Kulturen identisch behandelt werden. Wenn kulturunabhängige Zeichenfolgendaten wie XML-Tags, HTML-Tags, Benutzernamen, Dateipfade und die Namen von Systemobjekten so interpretiert werden, als wären sie kultursensitiv, kann Anwendungscode subtilen Fehlern, schlechter Leistung und in einigen Fällen Sicherheitsproblemen unterliegen.
In diesem Artikel werden die Methoden für die Sortierung, den Vergleich und die Schreibweise von Zeichenfolgen in .NET untersucht. Darüber hinaus werden Empfehlungen zum Auswählen einer entsprechenden Zeichenfolgen-Behandlungsmethode gegeben und weitere Informationen dazu bereitgestellt.
Empfehlungen für die Verwendung von Strings
Wenn Sie mit .NET entwickeln, folgen Sie diesen Empfehlungen, wenn Sie Zeichenfolgen vergleichen.
Tipp
Verschiedene zeichenfolgenbezogene Methoden führen Vergleiche durch. Beispiele hierfür sind String.Equals, String.Compare, String.IndexOfund String.StartsWith.
- Verwenden Sie Überladungen, die die Regeln für Zeichenfolgenvergleiche in Zeichenfolgenoperationen explizit angeben. Dies umfasst in der Regel das Aufrufen einer Methodenüberladung mit einem Parameter vom Typ StringComparison.
- Verwenden Sie StringComparison.Ordinal oder StringComparison.OrdinalIgnoreCase als sichere Standardeinstellung für kulturunabhängige Zeichenfolgenvergleiche.
- Verwenden Sie Vergleiche mit StringComparison.Ordinal oder StringComparison.OrdinalIgnoreCase für eine bessere Leistung.
- Verwenden Sie Zeichenfolgenoperationen, die auf StringComparison.CurrentCulture basieren, wenn Sie die Ausgabe für den Benutzer anzeigen.
- Verwenden Sie die nicht sprachlichen StringComparison.Ordinal Oder StringComparison.OrdinalIgnoreCase Werte anstelle von Zeichenfolgenvorgängen, die darauf CultureInfo.InvariantCulture basieren, wenn der Vergleich sprachlich irrelevant ist (z. B. symbolisch).
- Verwenden Sie die String.ToUpperInvariant Methode anstelle der String.ToLowerInvariant Methode, wenn Sie Zeichenfolgen für den Vergleich normalisieren.
- Verwenden Sie eine Überladung der String.Equals Methode, um zu testen, ob zwei Zeichenfolgen gleich sind.
- Verwenden Sie die Methoden String.Compare und String.CompareTo, um Zeichenfolgen zu sortieren, nicht um auf Gleichheit zu prüfen.
- Verwenden Sie kultursensitive Formatierung, um Nicht-Zeichenfolgendaten wie Zahlen und Datumsangaben in einer Benutzeroberfläche anzuzeigen. Verwenden Sie die Formatierung mit der invarianten Kultur , um Nicht-Zeichenfolgendaten in Zeichenfolgenform beizubehalten.
Vermeiden Sie beim Vergleichen von Zeichenfolgen die folgenden Methoden:
- Verwenden Sie keine Überladungen, die die Regeln für Zeichenfolgenvergleiche in Zeichenfolgenoperationen nicht explizit oder implizit angeben.
- Verwenden Sie in den meisten Fällen keine Zeichenfolgenvorgänge, die auf StringComparison.InvariantCulture basieren. Eine der wenigen Ausnahmen besteht darin, dass Sie sprachlich aussagekräftige, aber kulturell agnostische Daten beibehalten.
- Verwenden Sie keine Überladung der String.Compare- oder CompareTo-Methode, und testen Sie auf einen Rückgabewert von null, um zu ermitteln, ob zwei Zeichenfolgen gleich sind.
Tipp
Die Codeanalyseregeln CA1307, CA1309 und CA1310 helfen beim Identifizieren von Anrufwebsites, bei denen ein linguistischer Vergleich unbeabsichtigt verwendet wird. Um sie zu aktivieren und Verstöße als Buildfehler anzuzeigen, legen Sie die folgenden Eigenschaften in der Projektdatei fest.
<PropertyGroup>
<AnalysisMode>All</AnalysisMode>
<WarningsAsErrors>$(WarningsAsErrors);CA1307;CA1309;CA1310</WarningsAsErrors>
</PropertyGroup>
Explizite Angabe von Zeichenfolgenvergleichen
Die meisten Zeichenfolgenmanipulationsmethoden in .NET sind überladen. In der Regel akzeptieren eine oder mehrere Überladungen Standardeinstellungen, während andere keine Standardwerte akzeptieren und stattdessen die genaue Art und Weise definieren, in der Zeichenfolgen verglichen oder bearbeitet werden sollen. Die meisten Methoden, die keine Standardwerte verwenden, enthalten einen Parameter vom Typ StringComparison. Dabei handelt es sich um eine Enumeration, die explizit Regeln für Zeichenfolgenvergleiche nach Kultur und Schreibweise angibt. In der folgenden Tabelle werden die StringComparison Aufzählungselemente beschrieben.
StringComparison Mitglied |
BESCHREIBUNG |
|---|---|
| CurrentCulture | Führt einen Vergleich mit der aktuellen Kultur unter Beachtung der Groß- und Kleinschreibung durch. |
| CurrentCultureIgnoreCase | Führt einen Vergleich mit der aktuellen Kultur ohne Beachtung der Groß- und Kleinschreibung durch. |
| InvariantCulture | Führt einen Vergleich mit der invarianten Kultur unter Beachtung der Groß- und Kleinschreibung durch. |
| InvariantCultureIgnoreCase | Führt einen Vergleich mit der invarianten Kultur ohne Beachtung der Groß- und Kleinschreibung durch. |
| Ordinal | Führt einen Ordinalvergleich aus. |
| OrdinalIgnoreCase | Führt einen Ordinalvergleich ohne Beachtung der Groß- und Kleinschreibung durch. |
Die Methode, die IndexOf beispielsweise den Index einer Teilzeichenfolge in einem String Objekt zurückgibt, die entweder einem Zeichen oder einer Zeichenfolge entspricht, weist neun Überladungen auf:
- IndexOf(Char), IndexOf(Char, Int32) und IndexOf(Char, Int32, Int32), führt standardmäßig eine kulturunabhängige Ordinalsuche nach einem Zeichen in der Zeichenfolge unter Beachtung der Groß- und Kleinschreibung durch.
- IndexOf(String), IndexOf(String, Int32) und IndexOf(String, Int32, Int32), führt standardmäßig eine kulturabhängige Suche nach einer Teilzeichenfolge in der Zeichenfolge unter Beachtung der Groß- und Kleinschreibung durch.
- IndexOf(String, StringComparison), IndexOf(String, Int32, StringComparison) und IndexOf(String, Int32, Int32, StringComparison), die einen Parameter vom Typ StringComparison enthalten, der die Form des Vergleichs festlegt.
Es wird empfohlen, aus den folgenden Gründen eine Überladung auszuwählen, die keine Standardwerte verwendet:
Einige Überladungen mit Standardparametern (diejenigen, die in der Zeichenfolgeninstanz nach einer Char Zeichenfolge suchen) führen einen Ordinalvergleich aus, während andere (die nach einer Zeichenfolge in der Zeichenfolgeninstanz suchen) kultursensitiv sind. Es ist schwierig zu merken, welche Methode welchen Standardwert verwendet, und es ist einfach, die Überladungen zu verwechseln.
Die Absicht des Codes, der auf Standardwerten für Methodenaufrufe basiert, ist nicht eindeutig. Im folgenden Beispiel, das auf Standardwerten basiert, ist es schwierig zu wissen, ob der Entwickler tatsächlich einen Ordinal- oder einen sprachlichen Vergleich von zwei Zeichenfolgen beabsichtigt hat, oder ob ein Fallunterschied zwischen
url.Schemeund "https" den Test für die Gleichheit zurückgebenfalsekann.Uri url = new("https://learn.microsoft.com/"); // Incorrect if (string.Equals(url.Scheme, "https")) { // ...Code to handle HTTPS protocol. }Dim url As New Uri("https://learn.microsoft.com/") ' Incorrect If String.Equals(url.Scheme, "https") Then ' ...Code to handle HTTPS protocol. End If
Im Allgemeinen wird empfohlen, eine Methode aufzurufen, die nicht auf Standardwerte angewiesen ist, da sie die Absicht des Codes eindeutig macht. Dies wiederum macht den Code lesbarer und erleichtert das Debuggen und Verwalten. Im folgenden Beispiel werden die Fragen zum vorherigen Beispiel behandelt. Es wird klar, dass ordinale Vergleiche verwendet werden und dass Unterschiede im Fall ignoriert werden.
Uri url = new("https://learn.microsoft.com/");
// Correct
if (string.Equals(url.Scheme, "https", StringComparison.OrdinalIgnoreCase))
{
// ...Code to handle HTTPS protocol.
}
Dim url As New Uri("https://learn.microsoft.com/")
' Incorrect
If String.Equals(url.Scheme, "https", StringComparison.OrdinalIgnoreCase) Then
' ...Code to handle HTTPS protocol.
End If
Details des Zeichenfolgenvergleichs
Zeichenfolgenvergleiche bilden den Kern zahlreicher Operationen, die sich auf Zeichenfolgen beziehen; dies gilt insbesondere für Sortierungen und Überprüfungen auf Gleichheit. Zeichenfolgen werden in einer bestimmten Reihenfolge sortiert: Wenn „my“ in einer sortierten Zeichenfolgenliste vor „string“ angezeigt wird, muss „my“ bei einem Vergleich einen kleineren oder gleichen Wert wie „string“ haben. Darüber hinaus definiert der Vergleich implizit die Gleichheit. Der Vergleichsvorgang gibt null für Zeichenfolgen zurück, die gleich sind. Dies kann so interpretiert werden, dass keine Zeichenfolge kleiner ist als die andere. Die meisten sinnvollen Vorgänge mit Zeichenfolgen umfassen eine oder beide dieser Prozeduren: Vergleich mit einer anderen Zeichenfolge und Ausführen eines klar definierten Sortiervorgangs.
Hinweis
Sie können die Sortiergewichtstabellen, eine Reihe von Textdateien herunterladen, die Informationen zu den Zeichengewichten enthalten, die in Sortier- und Vergleichsvorgängen für Windows-Betriebssysteme verwendet werden, und die Standardmäßige Unicode-Sortierelementtabelle, die neueste Version der Sortiergewichtstabelle für Linux und macOS. Die spezifische Version der Sortiergewichtstabelle unter Linux und macOS hängt von der Version der auf dem System installierten internationalen Komponenten für Unicode-Bibliotheken ab. Informationen zu ICU-Versionen und den von ihnen implementierten Unicode-Versionen finden Sie unter "ICU herunterladen".
Die Auswertung von zwei Zeichenfolgen für Gleichheit oder Sortierreihenfolge führt jedoch nicht zu einem einzigen, richtigen Ergebnis; das Ergebnis hängt von den Kriterien ab, die zum Vergleichen der Zeichenfolgen verwendet werden. Insbesondere können Zeichenfolgenvergleiche, die Ordinalvergleiche darstellen oder auf der Schreibweise und den Sortierungskonventionen der aktuellen oder der invarianten Kultur (eine auf der englischen Sprache basierende Kultur, die nicht von einem Gebietsschema abhängig ist) basieren, zu unterschiedlichen Ergebnissen führen.
Darüber hinaus können Zeichenfolgenvergleiche, die verschiedene Versionen von .NET verwenden oder .NET auf unterschiedlichen Betriebssystemen oder Betriebssystemversionen verwenden, unterschiedliche Ergebnisse zurückgeben. .NET verwendet die International Components for Unicode (ICU)-Bibliothek für linguistische Zeichenfolgenvergleiche auf allen unterstützten Plattformen. Weitere Informationen finden Sie unter "Strings" und "Unicode Standard " und ".NET Globalization" und "ICU".
Zeichenkettenvergleiche, die die aktuelle Kultur verwenden
Ein Kriterium für Zeichenfolgenvergleiche stellt die Verwendung der Konventionen der aktuellen Kultur dar. Vergleiche, die auf der aktuellen Kultur basieren, verwenden die aktuelle Kultur oder das aktuelle Gebietsschema des Threads. Wenn der/die Benutzer*in die Kultur nicht festlegt, wird standardmäßig die Einstellung des Betriebssystems verwendet. Sie sollten immer Vergleiche verwenden, die auf der aktuellen Kultur basieren, wenn Daten sprachlich relevant sind und wenn sie kultursensitive Benutzerinteraktionen widerspiegelt.
Das Verhalten im Hinblick auf Vergleiche und die Groß- und Kleinschreibung in .NET ändert sich jedoch, wenn sich die Kultur ändert. Dies geschieht, wenn eine Anwendung auf einem Computer ausgeführt wird, der eine andere Kultur aufweist als der Computer, auf dem die Anwendung entwickelt wurde, oder wenn der ausgeführte Thread seine Kultur ändert. Dieses Verhalten ist beabsichtigt, aber es bleibt für viele Entwickler nicht offensichtlich. Im folgenden Beispiel werden Unterschiede in der Sortierreihenfolge zwischen der US-amerikanischen Kultur ("en-US") und der schwedischen ("sv-SE") veranschaulicht. Beachten Sie, dass die Wörter "ångström", "Windows" und "Visual Studio" an verschiedenen Positionen in den sortierten Zeichenfolgenarrays angezeigt werden.
using System.Globalization;
// Words to sort
string[] values= { "able", "ångström", "apple", "Æble",
"Windows", "Visual Studio" };
// Current culture
Array.Sort(values);
DisplayArray(values);
// Change culture to Swedish (Sweden)
string originalCulture = CultureInfo.CurrentCulture.Name;
Thread.CurrentThread.CurrentCulture = new CultureInfo("sv-SE");
Array.Sort(values);
DisplayArray(values);
// Restore the original culture
Thread.CurrentThread.CurrentCulture = new CultureInfo(originalCulture);
static void DisplayArray(string[] values)
{
Console.WriteLine($"Sorting using the {CultureInfo.CurrentCulture.Name} culture:");
foreach (string value in values)
Console.WriteLine($" {value}");
Console.WriteLine();
}
// The example displays the following output:
// Sorting using the en-US culture:
// able
// Æble
// ångström
// apple
// Visual Studio
// Windows
//
// Sorting using the sv-SE culture:
// able
// apple
// Visual Studio
// Windows
// ångström
// Æble
Imports System.Globalization
Imports System.Threading
Module Program
Sub Main()
' Words to sort
Dim values As String() = {"able", "ångström", "apple", "Æble",
"Windows", "Visual Studio"}
' Current culture
Array.Sort(values)
DisplayArray(values)
' Change culture to Swedish (Sweden)
Dim originalCulture As String = CultureInfo.CurrentCulture.Name
Thread.CurrentThread.CurrentCulture = New CultureInfo("sv-SE")
Array.Sort(values)
DisplayArray(values)
' Restore the original culture
Thread.CurrentThread.CurrentCulture = New CultureInfo(originalCulture)
End Sub
Sub DisplayArray(values As String())
Console.WriteLine($"Sorting using the {CultureInfo.CurrentCulture.Name} culture:")
For Each value As String In values
Console.WriteLine($" {value}")
Next
Console.WriteLine()
End Sub
End Module
' The example displays the following output:
' Sorting using the en-US culture:
' able
' Æble
' ångström
' apple
' Visual Studio
' Windows
'
' Sorting using the sv-SE culture:
' able
' apple
' Visual Studio
' Windows
' ångström
' Æble
Vergleiche mit der aktuellen Kultur, bei denen nicht zwischen Groß- und Kleinschreibung unterschieden wird, entsprechen kulturabhängigen Vergleichen, ignorieren jedoch die Schreibweise, die von der aktuellen Kultur des Threads vorgegeben wird. Dieses Verhalten kann sich auch in Sortierreihenfolgen manifestieren.
Vergleiche, die aktuelle Kultursemantik verwenden, sind die Standardeinstellung für die folgenden Methoden:
- String.Compare-Überladungen, die keinen StringComparison-Parameter enthalten.
- String.CompareTo-Überladungen.
- Die Standardmethode String.StartsWith(String) und die String.StartsWith(String, Boolean, CultureInfo) Methode mit einem
nullCultureInfo Parameter. - Die Standardmethode String.EndsWith(String) und die String.EndsWith(String, Boolean, CultureInfo) Methode mit einem
nullCultureInfo Parameter. - String.IndexOf-Überladungen, die String als Suchparameter akzeptieren und keinen StringComparison-Parameter aufweisen.
- String.LastIndexOf-Überladungen, die String als Suchparameter akzeptieren und keinen StringComparison-Parameter aufweisen.
In jedem Fall wird empfohlen, eine Überladung mit einem StringComparison -Parameter aufzurufen, um den Zweck des Methodenaufrufs eindeutig anzugeben.
Subtile und nicht so subtile Fehler können entstehen, wenn nicht-sprachliche Zeichenfolgendaten sprachlich interpretiert werden oder wenn Zeichenfolgendaten aus einer bestimmten Kultur mit den Konventionen einer anderen Kultur interpretiert werden. Das kanonische Beispiel ist das Turkish-I Problem.
Für fast alle lateinischen Alphabete, einschließlich US-Englisch, ist das Zeichen "i" (\u0069) die Kleinschreibung des Zeichens "I" (\u0049). Diese Regel zur Groß- und Kleinschreibung wird von Entwicklern, die in den entsprechenden Kulturen programmieren, leicht als Standard zugrunde gelegt. Das türkische Alphabet ("tr-TR") enthält jedoch ein "I mit einem Punkt" Zeichen "İ" (\u0130), das die Hauptversion von "i" ist. Türkisch enthält auch den Kleinbuchstaben "i ohne Punkt", "ı" (\u0131), der beim Großschreiben zu "I" wird. Dieses Verhalten tritt auch in der Aserbaidschanischen Kultur ("az") auf.
Daher sind Annahmen über die Großschreibung von "i" oder Kleinschreibung "I" unter allen Kulturen nicht anwendbar. Wenn Sie die Standardüberladungen für Zeichenfolgenvergleichsroutinen verwenden, weisen diese je nach der zugrunde liegenden Kultur Unterschiede auf. Bei einem Vergleich nicht linguistischer Daten kann die Verwendung der Standardüberladungen zu unerwünschten Ergebnissen führen. Dies wird durch den folgenden Versuch veranschaulicht, einen Vergleich der Zeichenfolgen „bill“ und „BILL“ ohne Berücksichtigung der Groß- und Kleinschreibung durchzuführen.
using System.Globalization;
string name = "Bill";
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
Console.WriteLine($"Culture = {Thread.CurrentThread.CurrentCulture.DisplayName}");
Console.WriteLine($" Is 'Bill' the same as 'BILL'? {name.Equals("BILL", StringComparison.OrdinalIgnoreCase)}");
Console.WriteLine($" Does 'Bill' start with 'BILL'? {name.StartsWith("BILL", true, null)}");
Console.WriteLine();
Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR");
Console.WriteLine($"Culture = {Thread.CurrentThread.CurrentCulture.DisplayName}");
Console.WriteLine($" Is 'Bill' the same as 'BILL'? {name.Equals("BILL", StringComparison.OrdinalIgnoreCase)}");
Console.WriteLine($" Does 'Bill' start with 'BILL'? {name.StartsWith("BILL", true, null)}");
//' The example displays the following output:
//'
//' Culture = English (United States)
//' Is 'Bill' the same as 'BILL'? True
//' Does 'Bill' start with 'BILL'? True
//'
//' Culture = Turkish (Türkiye)
//' Is 'Bill' the same as 'BILL'? True
//' Does 'Bill' start with 'BILL'? False
Imports System.Globalization
Imports System.Threading
Module Program
Sub Main()
Dim name As String = "Bill"
Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")
Console.WriteLine($"Culture = {Thread.CurrentThread.CurrentCulture.DisplayName}")
Console.WriteLine($" Is 'Bill' the same as 'BILL'? {name.Equals("BILL", StringComparison.OrdinalIgnoreCase)}")
Console.WriteLine($" Does 'Bill' start with 'BILL'? {name.StartsWith("BILL", True, Nothing)}")
Console.WriteLine()
Thread.CurrentThread.CurrentCulture = New CultureInfo("tr-TR")
Console.WriteLine($"Culture = {Thread.CurrentThread.CurrentCulture.DisplayName}")
Console.WriteLine($" Is 'Bill' the same as 'BILL'? {name.Equals("BILL", StringComparison.OrdinalIgnoreCase)}")
Console.WriteLine($" Does 'Bill' start with 'BILL'? {name.StartsWith("BILL", True, Nothing)}")
End Sub
End Module
' The example displays the following output:
'
' Culture = English (United States)
' Is 'Bill' the same as 'BILL'? True
' Does 'Bill' start with 'BILL'? True
'
' Culture = Turkish (Türkiye)
' Is 'Bill' the same as 'BILL'? True
' Does 'Bill' start with 'BILL'? False
Dieser Vergleich kann erhebliche Probleme verursachen, wenn die Kultureinstellung versehentlich in sicherheitsrelevanten Umgebungen verwendet wird, wie im folgenden Beispiel. Ein Methodenaufruf wie IsFileURI("file:") gibt true zurück, wenn die aktuelle Kultur US-Englisch ist, aber false, wenn die aktuelle Kultur Türkisch ist. In einem System mit der Kultur "Türkisch" wäre es daher vorstellbar, dass Sicherheitsvorkehrungen umgangen werden, die den Zugriff auf URIs blockieren, die mit "FILE:" beginnen und nicht zwischen Groß- und Kleinschreibung unterscheiden.
public static bool IsFileURI(string path) =>
path.StartsWith("FILE:", true, null);
Public Shared Function IsFileURI(path As String) As Boolean
Return path.StartsWith("FILE:", True, Nothing)
End Function
Da "file:" in diesem Fall als nicht sprachlicher, kulturunempfindlicher Bezeichner interpretiert werden soll, sollte der Code stattdessen wie im folgenden Beispiel dargestellt geschrieben werden:
public static bool IsFileURI(string path) =>
path.StartsWith("FILE:", StringComparison.OrdinalIgnoreCase);
Public Shared Function IsFileURI(path As String) As Boolean
Return path.StartsWith("FILE:", StringComparison.OrdinalIgnoreCase)
End Function
Ordinale Zeichenfolgenoperationen
Durch die Angabe des Werts StringComparison.Ordinal oder StringComparison.OrdinalIgnoreCase in einem Methodenaufruf wird ein nicht-linguistischer Vergleich vorgenommen, bei dem die Merkmale natürlicher Sprachen ignoriert werden. Bei Methoden, die mit diesen StringComparison -Werten aufgerufen werden, werden für Entscheidungen bezüglich Zeichenfolgenoperation einfache Bytevergleiche anstelle von Groß- und Kleinschreibung oder nach Kultur parametrisierten Entsprechungstabellen zugrunde gelegt. In den meisten Fällen passt dieser Ansatz am besten zur beabsichtigten Interpretation von Zeichenfolgen, während Code schneller und zuverlässiger gestaltet wird.
Ordinalvergleiche sind Zeichenfolgenvergleiche, bei denen jedes Byte jeder Zeichenfolge ohne sprachliche Interpretation verglichen wird; Beispielsweise stimmt "Windows" nicht mit "Windows" überein. Dies ist im Wesentlichen ein Aufruf der C-Laufzeitfunktion strcmp . Verwenden Sie diesen Vergleich, wenn im Kontext festgelegt wird, dass Zeichenfolgen exakt übereinstimmen sollen, oder wenn eine konservative Übereinstimmungspolitik erforderlich ist. Darüber hinaus ist ordinaler Vergleich der schnellste Vergleichsvorgang, da beim Bestimmen eines Ergebnisses keine sprachlichen Regeln angewendet werden.
Ein OrdinalIgnoreCase Vergleicher arbeitet weiterhin auf einer Zeichen-für-Zeichen-Basis, beseitigt währenddessen jedoch Groß-/Kleinschreibungsunterschiede. Unter einem OrdinalIgnoreCase Vergleicher werden die Zeichenpaare 'd' und 'D' als gleich bewertet, ebenso wie die Zeichenpaare 'á' und 'Á'. Das nicht akzentuierte Zeichen 'a' ist jedoch nicht gleich dem akzentuierten Zeichen 'á'.
Einige Beispiele hierfür sind in der folgenden Tabelle aufgeführt:
| Zeichenfolge 1 | String 2 |
Ordinal Vergleich |
OrdinalIgnoreCase Vergleich |
|---|---|---|---|
"dog" |
"dog" |
gleich | gleich |
"dog" |
"Dog" |
Ungleich | gleich |
"resume" |
"résumé" |
nicht gleich | Ungleich |
Unicode ermöglicht auch, dass Zeichenfolgen mehrere unterschiedliche In-Memory-Darstellungen aufweisen. So kann z. B. eine e-akute (é) auf zwei mögliche Arten dargestellt werden:
- Ein einzelnes Literalzeichen
'é'(auch geschrieben als'\u00E9'). - Ein wörtlich akzentfreies Zeichen
'e', gefolgt von einem Kombinationsakzentmodifiziererzeichen'\u0301'.
Dies bedeutet, dass die folgenden vier Zeichenfolgen alle als angezeigt werden "résumé", obwohl ihre Bestandteile unterschiedlich sind. Die Zeichenfolgen verwenden eine Kombination aus Literalzeichen 'é' oder literalen unaccentierten 'e' Zeichen sowie dem kombinierten Akzentmodifizierer '\u0301'.
"r\u00E9sum\u00E9""r\u00E9sume\u0301""re\u0301sum\u00E9""re\u0301sume\u0301"
Bei einem Ordinalvergleich vergleicht sich keine dieser Zeichenfolgen als gleich. Dies liegt daran, dass sie alle unterschiedliche zugrunde liegende Zeichensequenzen enthalten, auch wenn sie auf dem Bildschirm gerendert, alle gleich aussehen.
Beim Ausführen eines string.IndexOf(..., StringComparison.Ordinal) Vorgangs sucht die Laufzeit nach einer exakten Teilzeichenfolgenübereinstimmung. Die Ergebnisse sind wie folgt.
Console.WriteLine("resume".IndexOf('e', StringComparison.Ordinal)); // "resume": prints '1'
Console.WriteLine("r\u00E9sum\u00E9".IndexOf('e', StringComparison.Ordinal)); // "résumé": prints '-1'
Console.WriteLine("r\u00E9sume\u0301".IndexOf('e', StringComparison.Ordinal)); // "résumé": prints '5'
Console.WriteLine("re\u0301sum\u00E9".IndexOf('e', StringComparison.Ordinal)); // "résumé": prints '1'
Console.WriteLine("re\u0301sume\u0301".IndexOf('e', StringComparison.Ordinal)); // "résumé": prints '1'
Console.WriteLine("resume".IndexOf('e', StringComparison.OrdinalIgnoreCase)); // "resume": prints '1'
Console.WriteLine("r\u00E9sum\u00E9".IndexOf('e', StringComparison.OrdinalIgnoreCase)); // "résumé": prints '-1'
Console.WriteLine("r\u00E9sume\u0301".IndexOf('e', StringComparison.OrdinalIgnoreCase)); // "résumé": prints '5'
Console.WriteLine("re\u0301sum\u00E9".IndexOf('e', StringComparison.OrdinalIgnoreCase)); // "résumé": prints '1'
Console.WriteLine("re\u0301sume\u0301".IndexOf('e', StringComparison.OrdinalIgnoreCase)); // "résumé": prints '1'
Sub IndexOfExample()
Console.WriteLine("resume".IndexOf("e"c, StringComparison.Ordinal)) ' "resume": prints '1'
Console.WriteLine(("r" & ChrW(&HE9) & "sum" & ChrW(&HE9)).IndexOf("e"c, StringComparison.Ordinal)) ' "résumé": prints '-1'
Console.WriteLine(("r" & ChrW(&HE9) & "sume" & ChrW(&H301)).IndexOf("e"c, StringComparison.Ordinal)) ' "résumé": prints '5'
Console.WriteLine(("re" & ChrW(&H301) & "sum" & ChrW(&HE9)).IndexOf("e"c, StringComparison.Ordinal)) ' "résumé": prints '1'
Console.WriteLine(("re" & ChrW(&H301) & "sume" & ChrW(&H301)).IndexOf("e"c, StringComparison.Ordinal)) ' "résumé": prints '1'
Console.WriteLine("resume".IndexOf("e"c, StringComparison.OrdinalIgnoreCase)) ' "resume": prints '1'
Console.WriteLine(("r" & ChrW(&HE9) & "sum" & ChrW(&HE9)).IndexOf("e"c, StringComparison.OrdinalIgnoreCase)) ' "résumé": prints '-1'
Console.WriteLine(("r" & ChrW(&HE9) & "sume" & ChrW(&H301)).IndexOf("e"c, StringComparison.OrdinalIgnoreCase)) ' "résumé": prints '5'
Console.WriteLine(("re" & ChrW(&H301) & "sum" & ChrW(&HE9)).IndexOf("e"c, StringComparison.OrdinalIgnoreCase)) ' "résumé": prints '1'
Console.WriteLine(("re" & ChrW(&H301) & "sume" & ChrW(&H301)).IndexOf("e"c, StringComparison.OrdinalIgnoreCase)) ' "résumé": prints '1'
End Sub
Ordninale Such- und Vergleichsroutinen werden niemals von den Kultureinstellungen des aktuellen Threads beeinflusst.
Zeichenfolgen in .NET können eingebettete NULL-Zeichen (und andere nicht druckfreie Zeichen) enthalten. Einer der deutlichsten Unterschiede zwischen Ordinal- und kultursensiblen Vergleichs (einschließlich Vergleichen, die die invariante Kultur verwenden) betrifft die Behandlung eingebetteter Nullzeichen in einer Zeichenfolge. Diese Zeichen werden ignoriert, wenn Sie die Methoden String.Compare und String.Equals für kultursensitive Vergleiche verwenden (einschließlich solcher, die die invariante Kultur verwenden). Daher können Zeichenfolgen, die eingebettete NULL-Zeichen enthalten, als gleich zu Zeichenfolgen betrachtet werden, die keine enthalten. Eingebettete nicht druckende Zeichen können für Zeichenfolgenvergleichsmethoden wie String.StartsWith übersprungen werden.
Von Bedeutung
Obwohl Zeichenfolgenvergleichsmethoden eingebettete NULL-Zeichen ignorieren, ignorieren Zeichenfolgensuchmethoden wie String.Contains, String.EndsWith, String.IndexOf, String.LastIndexOf und String.StartsWith diese nicht.
Im folgenden Beispiel wird ein kultursensitiver Vergleich der Zeichenfolge "Aa" mit einer ähnlichen Zeichenfolge ausgeführt, die mehrere eingebettete Nullzeichen zwischen "A" und "a" enthält, und zeigt, wie die beiden Zeichenfolgen gleich angesehen werden:
string str1 = "Aa";
string str2 = "A" + new string('\u0000', 3) + "a";
Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo("en-us");
Console.WriteLine($"Comparing '{str1}' ({ShowBytes(str1)}) and '{str2}' ({ShowBytes(str2)}):");
Console.WriteLine(" With String.Compare:");
Console.WriteLine($" Current Culture: {string.Compare(str1, str2, StringComparison.CurrentCulture)}");
Console.WriteLine($" Invariant Culture: {string.Compare(str1, str2, StringComparison.InvariantCulture)}");
Console.WriteLine(" With String.Equals:");
Console.WriteLine($" Current Culture: {string.Equals(str1, str2, StringComparison.CurrentCulture)}");
Console.WriteLine($" Invariant Culture: {string.Equals(str1, str2, StringComparison.InvariantCulture)}");
string ShowBytes(string value)
{
string hexString = string.Empty;
for (int index = 0; index < value.Length; index++)
{
string result = Convert.ToInt32(value[index]).ToString("X4");
result = string.Concat(" ", result.Substring(0,2), " ", result.Substring(2, 2));
hexString += result;
}
return hexString.Trim();
}
// The example displays the following output:
// Comparing 'Aa' (00 41 00 61) and 'Aa' (00 41 00 00 00 00 00 00 00 61):
// With String.Compare:
// Current Culture: 0
// Invariant Culture: 0
// With String.Equals:
// Current Culture: True
// Invariant Culture: True
Module Program
Sub Main()
Dim str1 As String = "Aa"
Dim str2 As String = "A" & New String(Convert.ToChar(0), 3) & "a"
Console.WriteLine($"Comparing '{str1}' ({ShowBytes(str1)}) and '{str2}' ({ShowBytes(str2)}):")
Console.WriteLine(" With String.Compare:")
Console.WriteLine($" Current Culture: {String.Compare(str1, str2, StringComparison.CurrentCulture)}")
Console.WriteLine($" Invariant Culture: {String.Compare(str1, str2, StringComparison.InvariantCulture)}")
Console.WriteLine(" With String.Equals:")
Console.WriteLine($" Current Culture: {String.Equals(str1, str2, StringComparison.CurrentCulture)}")
Console.WriteLine($" Invariant Culture: {String.Equals(str1, str2, StringComparison.InvariantCulture)}")
End Sub
Function ShowBytes(str As String) As String
Dim hexString As String = String.Empty
For ctr As Integer = 0 To str.Length - 1
Dim result As String = Convert.ToInt32(str.Chars(ctr)).ToString("X4")
result = String.Concat(" ", result.Substring(0, 2), " ", result.Substring(2, 2))
hexString &= result
Next
Return hexString.Trim()
End Function
' The example displays the following output:
' Comparing 'Aa' (00 41 00 61) and 'Aa' (00 41 00 00 00 00 00 00 00 61):
' With String.Compare:
' Current Culture: 0
' Invariant Culture: 0
' With String.Equals:
' Current Culture: True
' Invariant Culture: True
End Module
Die Zeichenfolgen gelten jedoch nicht als gleich, wenn Sie einen Ordinalvergleich verwenden, wie das folgende Beispiel zeigt.
string str1 = "Aa";
string str2 = "A" + new String('\u0000', 3) + "a";
Console.WriteLine($"Comparing '{str1}' ({ShowBytes(str1)}) and '{str2}' ({ShowBytes(str2)}):");
Console.WriteLine(" With String.Compare:");
Console.WriteLine($" Ordinal: {string.Compare(str1, str2, StringComparison.Ordinal)}");
Console.WriteLine(" With String.Equals:");
Console.WriteLine($" Ordinal: {string.Equals(str1, str2, StringComparison.Ordinal)}");
string ShowBytes(string str)
{
string hexString = string.Empty;
for (int ctr = 0; ctr < str.Length; ctr++)
{
string result = Convert.ToInt32(str[ctr]).ToString("X4");
result = " " + result.Substring(0, 2) + " " + result.Substring(2, 2);
hexString += result;
}
return hexString.Trim();
}
// The example displays the following output:
// Comparing 'Aa' (00 41 00 61) and 'A a' (00 41 00 00 00 00 00 00 00 61):
// With String.Compare:
// Ordinal: 97
// With String.Equals:
// Ordinal: False
Module Program
Sub Main()
Dim str1 As String = "Aa"
Dim str2 As String = "A" & New String(Convert.ToChar(0), 3) & "a"
Console.WriteLine($"Comparing '{str1}' ({ShowBytes(str1)}) and '{str2}' ({ShowBytes(str2)}):")
Console.WriteLine(" With String.Compare:")
Console.WriteLine($" Ordinal: {String.Compare(str1, str2, StringComparison.Ordinal)}")
Console.WriteLine(" With String.Equals:")
Console.WriteLine($" Ordinal: {String.Equals(str1, str2, StringComparison.Ordinal)}")
End Sub
Function ShowBytes(str As String) As String
Dim hexString As String = String.Empty
For ctr As Integer = 0 To str.Length - 1
Dim result As String = Convert.ToInt32(str.Chars(ctr)).ToString("X4")
result = String.Concat(" ", result.Substring(0, 2), " ", result.Substring(2, 2))
hexString &= result
Next
Return hexString.Trim()
End Function
' The example displays the following output:
' Comparing 'Aa' (00 41 00 61) and 'A a' (00 41 00 00 00 00 00 00 00 61):
' With String.Compare:
' Ordinal: 97
' With String.Equals:
' Ordinal: False
End Module
Ordinalvergleiche, bei denen die Groß- und Kleinschreibung nicht beachtet wird, folgen als nächster konservativer Ansatz. Diese Vergleichen ignorieren die Groß- und Kleinschreibung i. d. R.; "windows" entspricht dann beispielsweise "Windows". Im Hinblick auf ASCII-Zeichen entspricht diese Richtlinie StringComparison.Ordinal, mit der Ausnahme, dass die üblichen ASCII-Schreibungsregeln nicht beachtet wird. Daher entspricht jedes Zeichen in [A, Z] (\u0041-\u005A) dem entsprechenden Zeichen in [a,z] (\u0061-\007A). Die Groß- und Kleinschreibung außerhalb des ASCII-Bereichs verwendet die Tabellen der invarianten Kultur. Daher der folgende Vergleich:
string.Compare(strA, strB, StringComparison.OrdinalIgnoreCase);
String.Compare(strA, strB, StringComparison.OrdinalIgnoreCase)
ist gleichbedeutend mit (aber schneller als) diesem Vergleich:
string.Compare(strA.ToUpperInvariant(), strB.ToUpperInvariant(), StringComparison.Ordinal);
String.Compare(strA.ToUpperInvariant(), strB.ToUpperInvariant(), StringComparison.Ordinal)
Diese Vergleiche sind immer noch sehr schnell.
Beide StringComparison.Ordinal und StringComparison.OrdinalIgnoreCase verwenden die binären Werte direkt und sind am besten geeignet für den Abgleich. Wenn Sie sich über Ihre Vergleichseinstellungen nicht sicher sind, verwenden Sie einen dieser beiden Werte. Da sie jedoch einen Byte-By-Byte-Vergleich durchführen, sortieren sie nicht nach einer sprachlichen Sortierreihenfolge (z. B. einem englischen Wörterbuch), sondern nach einer binären Sortierreihenfolge. Die Ergebnisse können in den meisten Kontexten merkwürdig aussehen, wenn sie Benutzern angezeigt werden.
Ordnungssemantik ist die Standardeinstellung für String.Equals Überladungen, die kein StringComparison Argument enthalten (einschließlich des Gleichheitsoperators). In jedem Fall wird empfohlen, eine Überladung mit einem StringComparison -Parameter aufzurufen.
Linguistische Zeichenfolgenvergleiche
Linguistische Such- und Vergleichsroutinen dekompilieren eine Zeichenfolge in Sortierungselemente und führen Suchvorgänge oder Vergleiche für diese Elemente durch. Es gibt nicht unbedingt eine 1:1-Zuordnung zwischen den Zeichen einer Zeichenfolge und den zugehörigen Sortierungselementen. Eine Zeichenfolge der Länge 2 kann z. B. nur aus einem einzelnen Sortierungselement bestehen. Wenn zwei Zeichenfolgen in sprachbewusster Weise verglichen werden, überprüft der Vergleicher, ob die Kollationselemente der beiden Zeichenfolgen dieselbe semantische Bedeutung haben, auch wenn die wörtlichen Zeichen der Zeichenfolge unterschiedlich sind.
Betrachten Sie die Zeichenfolge "résumé" und die vier verschiedenen Darstellungen, die im vorherigen Abschnitt beschrieben werden. In der folgenden Tabelle sind die einzelnen Repräsentationen dargestellt, die in ihre Sortierungselemente unterteilt sind.
| String | Als Sortierungselemente |
|---|---|
"r\u00E9sum\u00E9" |
"r" + "\u00E9" + "s" + "u" + "m" + "\u00E9" |
"r\u00E9sume\u0301" |
"r" + "\u00E9" + "s" + "u" + "m" + "e\u0301" |
"re\u0301sum\u00E9" |
"r" + "e\u0301" + "s" + "u" + "m" + "\u00E9" |
"re\u0301sume\u0301" |
"r" + "e\u0301" + "s" + "u" + "m" + "e\u0301" |
Ein Sortierungselement entspricht in etwa dem, was Leser als ein einzelnes Zeichen oder eine Zeichenkombination betrachten würden. Es ähnelt konzeptuell einem Grapheme-Cluster , umfasst aber einen etwas größeren Regenschirm.
Unter einem sprachlichen Vergleich sind genaue Übereinstimmungen nicht erforderlich. Sortierungselemente werden stattdessen basierend auf ihrer semantischen Bedeutung verglichen. Beispielsweise behandelt ein linguistischer Vergleich die Teilzeichenfolgen "\u00E9" und "e\u0301" gleich, da beide semantisch "ein Kleinbuchstaben e mit einem akuten Akzentmodifizierer" bedeuten. Dadurch kann die IndexOf Methode die Teilzeichenfolge "e\u0301" innerhalb einer größeren Zeichenfolge abgleichen, die die semantisch gleichwertige Teilzeichenfolge "\u00E9"enthält, wie im folgenden Codebeispiel gezeigt.
Console.WriteLine("r\u00E9sum\u00E9".IndexOf("e")); // "résumé": prints '-1' (not found)
Console.WriteLine("r\u00E9sum\u00E9".IndexOf("\u00E9")); // "résumé": prints '1'
Console.WriteLine("\u00E9".IndexOf("e\u0301")); // prints '0'
Sub IndexOfStringExample()
Console.WriteLine(("r" & ChrW(&HE9) & "sum" & ChrW(&HE9)).IndexOf("e")) ' "résumé": prints '-1' (not found)
Console.WriteLine(("r" & ChrW(&HE9) & "sum" & ChrW(&HE9)).IndexOf(ChrW(&HE9).ToString())) ' "résumé": prints '1'
Console.WriteLine(ChrW(&HE9).ToString().IndexOf("e" & ChrW(&H301))) ' prints '0'
End Sub
Aus diesem Grund können zwei Zeichenketten unterschiedlicher Länge als gleich gelten, wenn ein linguistischer Vergleich verwendet wird. Aufrufer sollten sich nicht um spezielle Logik kümmern, die sich mit der Länge von Zeichenfolgen in solchen Szenarien befasst.
Kulturbewusste Such- und Vergleichsroutinen sind eine besondere Form von sprachlichen Such- und Vergleichsroutinen. Unter einem kulturbewussten Vergleich wird das Konzept eines Sortierungselements erweitert, um Spezifische Informationen für die angegebene Kultur einzuschließen.
Wenn im ungarischen Alphabet die beiden Zeichen <dz> unmittelbar hintereinander erscheinen, gelten sie als ein eigener Buchstabe, der sich sowohl von <d> als auch von <z> unterscheidet. Dies bedeutet, dass ein ungarischer kulturbezogener Vergleichsoperator das Element "<dz>" in einer Zeichenfolge als ein einzelnes Sortierungselement behandelt.
| String | Als Sortierungselemente | Bemerkungen |
|---|---|---|
"endz" |
"e" + "n" + "d" + "z" |
(unter Verwendung eines standardmäßigen sprachlichen Vergleichs) |
"endz" |
"e" + "n" + "dz" |
(mit einem ungarischen kultursensitiven Vergleicher) |
Bei Verwendung eines ungarischen kultursensiblen Vergleichs endet die Zeichenfolge "endz"nicht mit der Teilzeichenfolge "z", da <dz> und <z> als Kollationselemente mit unterschiedlicher semantischer Bedeutung betrachtet werden.
// Set thread culture to Hungarian
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("hu-HU");
Console.WriteLine("endz".EndsWith("z")); // Prints 'False'
// Set thread culture to invariant culture
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
Console.WriteLine("endz".EndsWith("z")); // Prints 'True'
' Set thread culture to Hungarian
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("hu-HU")
Console.WriteLine("endz".EndsWith("z")) ' Prints 'False'
' Set thread culture to invariant culture
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture
Console.WriteLine("endz".EndsWith("z")) ' Prints 'True'
Hinweis
- Verhalten: Sprachliche und kulturbewusste Vergleicher können von Zeit zu Zeit Verhaltensanpassungen erfahren. Sowohl ICU als auch die ältere Windows NLS-Einrichtung werden aktualisiert, um zu berücksichtigen, wie sich die Weltsprachen ändern. Weitere Informationen finden Sie im Blogbeitrag Locale (Culture) data churn. Das Verhalten des Ordinal-Vergleichers wird sich nie ändern, da er eine genaue bitweise Suche und einen Vergleich durchführt. Das Verhalten des OrdinalIgnoreCase-Vergleichs kann sich jedoch ändern, während Unicode wächst, um mehr Zeichensätze zu integrieren und um Auslassungen in den bestehenden Groß-/Kleinschreibungsdaten zu korrigieren.
-
Verwendung: Die Vergleichsfunktionen
StringComparison.InvariantCultureundStringComparison.InvariantCultureIgnoreCasesind sprachliche Vergleichsfunktionen, die keine Berücksichtigung der Kulturen finden. Das heißt, diese Vergleicher verstehen Konzepte wie das Akzentzeichen é mit mehreren möglichen zugrunde liegenden Darstellungen und dass alle derartigen Darstellungen gleich behandelt werden sollten. Aber nicht kulturbewusste linguistische Vergleicher enthalten keine spezielle Behandlung für <dz> im Unterschied zu <d> oder <z>, wie oben gezeigt. Sie werden auch keine Sonderzeichen wie das Deutsche Eszett (ß) verwenden.
.NET bietet auch den invarianten Globalisierungsmodus. Dieser Opt-In-Modus deaktiviert Codepfade, die mit sprachlichen Such- und Vergleichsroutinen umgehen. In diesem Modus verwenden alle Vorgänge Ordinal- oder OrdinalIgnoreCase-Verhalten, unabhängig davon, welches CultureInfo oder StringComparison Argument der Aufrufer angegeben hat. Weitere Informationen finden Sie unter Laufzeitkonfigurationsoptionen für Globalisierung und .NET Core Globalization Invariant Mode.
Zeichenfolgenoperationen mit der invarianten Kultur
Vergleiche mit der invarianten Kultur verwenden die CompareInfo -Eigenschaft, die von der statischen CultureInfo.InvariantCulture -Eigenschaft zurückgegeben wird. Dieses Verhalten ist auf allen Systemen identisch; es übersetzt alle Zeichen außerhalb seines Bereichs in das, was es als gleichwertige Invariante-Zeichen betrachtet. Diese Richtlinie kann für das kulturübergreifende Beibehalten eines Satzes von Zeichenfolgenverhalten hilfreich sein, führt jedoch oftmals zu unerwarteten Ergebnissen.
Vergleiche mit der invarianten Kultur, bei denen nicht zwischen Groß- und Kleinschreibung unterschieden wird, verwenden für Vergleichsinformationen ebenfalls die statische CompareInfo -Eigenschaft, die von der statischen CultureInfo.InvariantCulture -Eigenschaft zurückgegeben wird. Alle Fallunterschiede zwischen diesen übersetzten Zeichen werden ignoriert.
Vergleiche, die StringComparison.InvariantCulture und StringComparison.Ordinal verwenden, funktionieren bei ASCII-Zeichenfolgen identisch. Allerdings trifft StringComparison.InvariantCulture sprachliche Entscheidungen, die möglicherweise nicht für Zeichenfolgen geeignet sind, die als ein Satz von Bytes interpretiert werden müssen. Das CultureInfo.InvariantCulture.CompareInfo Objekt veranlasst die Compare Methode, bestimmte Zeichensätze als gleichwertig zu interpretieren. Beispielsweise ist die folgende Äquivalenz unter der invarianten Kultur gültig:
InvariantCulture: a + ̊ = å
Der LATEINISCHE KLEINBUCHSTABE A „a“ (\u0061) wird, wenn er sich neben dem KOMBINIERENDEN ÜBERGESETZTEN RING "+ " ̊" (\u030a) befindet, als LATEINISCHER KLEINBUCHSTABE MIT ÜBERGESETZTEM RING „å“ (\u00e5) interpretiert. Wie im folgenden Beispiel gezeigt, unterscheidet sich dieses Verhalten vom Ordinalvergleich.
string separated = "\u0061\u030a";
string combined = "\u00e5";
Console.WriteLine($"Equal sort weight of {separated} and {combined} using InvariantCulture: {string.Compare(separated, combined, StringComparison.InvariantCulture) == 0}");
Console.WriteLine($"Equal sort weight of {separated} and {combined} using Ordinal: {string.Compare(separated, combined, StringComparison.Ordinal) == 0}");
// The example displays the following output:
// Equal sort weight of a° and å using InvariantCulture: True
// Equal sort weight of a° and å using Ordinal: False
Module Program
Sub Main()
Dim separated As String = ChrW(&H61) & ChrW(&H30A)
Dim combined As String = ChrW(&HE5)
Console.WriteLine("Equal sort weight of {0} and {1} using InvariantCulture: {2}",
separated, combined,
String.Compare(separated, combined, StringComparison.InvariantCulture) = 0)
Console.WriteLine("Equal sort weight of {0} and {1} using Ordinal: {2}",
separated, combined,
String.Compare(separated, combined, StringComparison.Ordinal) = 0)
' The example displays the following output:
' Equal sort weight of a° and å using InvariantCulture: True
' Equal sort weight of a° and å using Ordinal: False
End Sub
End Module
Bei der Interpretation von Dateinamen, Cookies oder anderen Elementen, bei denen eine Kombination wie "å" angezeigt werden kann, bieten Ordinalvergleiche immer noch das transparenteste und passende Verhalten.
Im Gleichgewicht hat die invariante Kultur nur wenige Eigenschaften, die sie zum Vergleich nützlich machen. Der Vergleich erfolgt auf eine sprachlich relevante Weise, die verhindert, dass eine vollständige symbolische Äquivalenz garantiert wird, und ist daher keine Option für die Anzeige in irgendeiner Kultur. Einer der wenigen Gründe, StringComparison.InvariantCulture für den Vergleich zu verwenden, besteht darin, sortierte Daten für eine kulturübergreifend identische Anzeige aufrechtzuerhalten. Wenn beispielsweise eine große Datendatei, die eine Liste sortierter Bezeichner für die Anzeige enthält, eine Anwendung begleitet, erfordert das Hinzufügen zu dieser Liste eine Einfügung mit invarianter Sortierung.
Auswählen eines StringComparison-Mitglieds
In der folgenden Tabelle wird die Zuordnung von semantischem Zeichenfolgenkontext zu einem StringComparison-Enumerationsmember beschrieben:
| Daten | Verhalten | System.StringComparison-Entsprechung Wert |
|---|---|---|
| Interne Bezeichner, die die Groß- und Kleinschreibung beachten. Bezeichner in Standards wie XML und HTTP, die die Groß- und Kleinschreibung beachten. Sicherheitsbezogene Einstellungen, die die Groß- und Kleinschreibung beachten. |
Ein nicht sprachlicher Bezeichner, bei dem Bytes exakt übereinstimmen. | Ordinal |
| Interne Bezeichner, die die Groß- und Kleinschreibung nicht beachten. Bezeichner in Standards wie XML und HTTP, die die Groß- und Kleinschreibung nicht beachten. Dateipfade. Registrierungsschlüssel und Werte. Umgebungsvariablen. Ressourcenbezeichner (z. B. Handlenamen). Sicherheitsbezogene Einstellungen, die die Groß- und Kleinschreibung nicht beachten. |
Ein nicht sprachlicher Bezeichner, wenn der Fall irrelevant ist. | OrdinalIgnoreCase |
| Einige beibehaltene, sprachlich relevante Daten. Anzeige von linguistischen Daten, für die eine feste Sortierreihenfolge erforderlich ist. |
Kulturell agnostische Daten, die immer noch sprachlich relevant sind. | InvariantCulture -oder- InvariantCultureIgnoreCase |
| Daten, die dem Benutzer angezeigt werden. Die meisten Benutzereingaben. |
Daten, die lokale sprachliche Bräuche erfordern. | CurrentCulture -oder- CurrentCultureIgnoreCase |
Folgen für die Sicherheit
Wenn Ihre App Zeichenfolgen-APIs zum Filtern oder Zugriffssteuerung verwendet, verwenden Sie Ordinalvergleiche. Linguistische Vergleiche, die auf der aktuellen Kultur basieren, können zu unerwarteten Ergebnissen führen, die je nach Plattform und Gebietsschema variieren. Codemuster wie die folgenden können anfällig für Sicherheits-Exploits sein:
//
// THIS SAMPLE CODE IS INCORRECT.
// DO NOT USE IT IN PRODUCTION.
//
bool ContainsHtmlSensitiveCharacters(string input)
{
if (input.IndexOf("<") >= 0) { return true; }
if (input.IndexOf("&") >= 0) { return true; }
return false;
}
'
' THIS SAMPLE CODE IS INCORRECT.
' DO NOT USE IT IN PRODUCTION.
'
Function ContainsHtmlSensitiveCharacters(input As String) As Boolean
If input.IndexOf("<") >= 0 Then Return True
If input.IndexOf("&") >= 0 Then Return True
Return False
End Function
Da die string.IndexOf(string) Methode standardmäßig eine linguistische Suche verwendet, ist es möglich, dass eine Zeichenfolge ein Literalzeichen '<' oder '&' Zeichen enthält und string.IndexOf(string)-1 zurückgegeben wird, was angibt, dass die Suchunterzeichenfolge nicht gefunden wurde. Codeanalyseregeln CA1307 und CA1309 kennzeichnen solche Anrufwebsites und benachrichtigen den Entwickler, dass ein potenzielles Problem vorliegt.
Allgemeine Zeichenfolgenvergleichsmethoden in .NET
In den folgenden Abschnitten werden die Methoden beschrieben, die am häufigsten für den Zeichenfolgenvergleich verwendet werden.
String.Compare
Standard-Interpretation: StringComparison.CurrentCulture.
Da der Vorgang für die Zeichenfolgeninterpretation am wichtigsten ist, sollten alle Instanzen dieser Methodenaufrufe untersucht werden, um festzustellen, ob Zeichenfolgen gemäß der aktuellen Kultur interpretiert oder von der Kultur (symbolisch) getrennt werden sollen. In der Regel ist es letzteres, und stattdessen sollte ein StringComparison.Ordinal Vergleich verwendet werden.
Die System.Globalization.CompareInfo-Klasse, die von der CultureInfo.CompareInfo-Eigenschaft zurückgegeben wird, enthält auch eine Compare-Methode, die eine Vielzahl von Optionen für Übereinstimmungen (wie ordinale Vergleiche, Ignorieren von Leerzeichen, Ignorieren des Kana-Typs usw.) mittels der CompareOptions-Flag-Enumeration bereitstellt.
String.CompareTo
Standard-Interpretation: StringComparison.CurrentCulture.
Diese Methode bietet derzeit keine Überladung an, die einen StringComparison-Typ angibt. In der Regel ist es möglich, diese Methode in das empfohlene String.Compare(String, String, StringComparison) Formular zu konvertieren.
Typen, die die IComparable und IComparable<T> Schnittstellen implementieren, implementieren diese Methode. Da es nicht die Option eines StringComparison-Parameters bietet, erlauben Implementierungstypen häufig, dass ein StringComparer in ihrem Konstruktor festgelegt wird. Im folgenden Beispiel wird eine FileName Klasse definiert, deren Klassenkonstruktor einen StringComparer Parameter enthält. Dieses StringComparer Objekt wird dann in der FileName.CompareTo Methode verwendet.
class FileName : IComparable
{
private readonly StringComparer _comparer;
public string Name { get; }
public FileName(string name, StringComparer? comparer)
{
if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));
Name = name;
if (comparer != null)
_comparer = comparer;
else
_comparer = StringComparer.OrdinalIgnoreCase;
}
public int CompareTo(object? obj)
{
if (obj == null) return 1;
if (obj is not FileName)
return _comparer.Compare(Name, obj.ToString());
else
return _comparer.Compare(Name, ((FileName)obj).Name);
}
}
Class FileName
Implements IComparable
Private ReadOnly _comparer As StringComparer
Public ReadOnly Property Name As String
Public Sub New(name As String, comparer As StringComparer)
If (String.IsNullOrEmpty(name)) Then Throw New ArgumentNullException(NameOf(name))
Me.Name = name
If comparer IsNot Nothing Then
_comparer = comparer
Else
_comparer = StringComparer.OrdinalIgnoreCase
End If
End Sub
Public Function CompareTo(obj As Object) As Integer Implements IComparable.CompareTo
If obj Is Nothing Then Return 1
If TypeOf obj IsNot FileName Then
Return _comparer.Compare(Name, obj.ToString())
Else
Return _comparer.Compare(Name, DirectCast(obj, FileName).Name)
End If
End Function
End Class
String.Equals
Standard-Interpretation: StringComparison.Ordinal.
Mit der String -Klasse können Sie eine Überprüfung auf Gleichheit durchführen, indem Sie die Überladungen der statischen Equals -Methode oder der entsprechenden Instanzenmethode aufrufen, oder indem Sie den statischen Gleichheitsoperator verwenden. Die Überladungen und der Operator verwenden standardmäßig einen Ordinalvergleich . Es wird jedoch weiterhin empfohlen, eine Überladung aufzurufen, die den StringComparison Typ explizit angibt, auch wenn Sie einen Ordinalvergleich durchführen möchten. Dadurch wird das Durchsuchen von Code nach einer bestimmten Zeichenfolgeninterpretation erleichtert.
String.ToUpper und String.ToLower
Standard-Interpretation: StringComparison.CurrentCulture.
Die Methoden String.ToUpper() und String.ToLower() sollten mit besonderer Sorgfalt verwendet werden, da das Erzwingen der Groß- oder Kleinschreibung für eine Zeichenfolge oftmals unabhängig von der Groß-/Kleinschreibung als kleine Normalisierung für Zeichenfolgenvergleiche verwendet wird. Wenn dies der Fall ist, sollten Sie einen Vergleich ohne Beachtung der Groß- und Kleinschreibung in Erwägung ziehen.
Die String.ToUpperInvariant- und String.ToLowerInvariant-Methoden sind ebenfalls verfügbar. ToUpperInvariant wird standardmäßig zur Normalisierung der Schreibweise verwendet. Das Verhalten von Vergleichen mit StringComparison.OrdinalIgnoreCase setzt sich aus zwei Aufrufen zusammen: einem Aufruf von ToUpperInvariant für beide Zeichenfolgenargumente und einem Vergleich mithilfe von StringComparison.Ordinal.
Überladungen sind auch für die Konvertierung in Groß- und Kleinschreibung in einer bestimmten Kultur verfügbar. Dazu wird ein CultureInfo -Objekt übergeben, das die Kultur für die Methode darstellt.
Char.ToUpper und Char.ToLower
Standard-Interpretation: StringComparison.CurrentCulture.
Die Char.ToUpper(Char) und Char.ToLower(Char) Methoden funktionieren ähnlich wie die String.ToUpper() und String.ToLower() Methoden, die im vorherigen Abschnitt beschrieben wurden.
String.StartsWith und String.EndsWith
Standardinterpretation: StringComparison.CurrentCulture (wenn der erste Parameter ein stringist) oder StringComparison.Ordinal (wenn der erste Parameter ein charist).
Es gibt eine Inkonsistenz, in der die Standardüberladungen dieser Methoden Vergleiche durchführen. Überladungen, die einen char Parameter akzeptieren, führen einen Ordinalvergleich durch, aber Überladungen, die einen string Parameter akzeptieren, führen einen kultursensitiven Vergleich aus und ignorieren möglicherweise nicht druckbare Zeichen.
String.IndexOf und String.LastIndexOf
Standard-Interpretation: StringComparison.CurrentCulture.
Die Durchführung von Vergleichen durch die Standardüberladungen dieser Methoden ist nicht konsistent. Alle String.IndexOf Methoden und String.LastIndexOf Methoden, die einen Parameter enthalten, führen einen Char Ordinalvergleich aus, aber die Standard String.IndexOf - und String.LastIndexOf Methoden, die einen String Parameter enthalten, führen einen kultursensitiven Vergleich aus.
Wenn Sie die Methode String.IndexOf(String) oder String.LastIndexOf(String) aufrufen und eine Zeichenfolge übergeben, die in der aktuellen Instanz gefunden werden soll, empfehlen wir, eine Überladung aufzurufen, die den StringComparison-Typ explizit angibt. Überladungen, die ein Char Argument enthalten, erlauben es Ihnen nicht, einen Typ StringComparison anzugeben.
String.Contains
Standard-Interpretation: StringComparison.Ordinal.
Im Gegensatz zu String.IndexOf verwendet die String.Contains-Methode standardmäßig einen Ordinalvergleich für beide char und string Überladungen. Sie sollten jedoch weiterhin ein explizites StringComparison Argument übergeben, wenn die Absicht wichtig ist, um das Verhalten auf der Anrufwebsite deutlich zu machen.
MemoryExtensions.AsSpan.IndexOfAny und der SearchValues<T> Typ
.NET 8 hat den SearchValues<T> Typ eingeführt, der eine optimierte Lösung für die Suche nach bestimmten Zeichengruppen oder Byte innerhalb von Spannen bereitstellt.
Wenn Sie eine Zeichenfolge mit einem festen Satz bekannter Werte wiederholt vergleichen, sollten Sie die SearchValues<T>.Contains(T) Methode anstelle von verketteten Vergleichen oder LINQ-basierten Ansätzen verwenden.
SearchValues<T> kann interne Nachschlagestrukturen vorkompilieren und die Vergleichslogik basierend auf den bereitgestellten Werten optimieren. Um leistungsvorteile anzuzeigen, erstellen und zwischenspeichern Sie die SearchValues<string> Instanz einmal, und verwenden Sie sie dann für Vergleiche wieder:
using System.Buffers;
namespace ExampleCode;
internal partial class DemoCode
{
private static readonly SearchValues<string> Commands =
SearchValues.Create(
["start", "run", "go", "begin", "commence"],
StringComparison.OrdinalIgnoreCase);
void ProcessCommand(string command)
{
if (Commands.Contains(command))
{
// ...
}
}
}
Imports System.Buffers
Namespace ExampleCode
Partial Friend Class DemoCode
Private Shared ReadOnly Commands As SearchValues(Of String) =
SearchValues.Create(
{"start", "run", "go", "begin", "commence"},
StringComparison.OrdinalIgnoreCase)
Sub ProcessCommand(command As String)
If Commands.Contains(command) Then
' ...
End If
End Sub
End Class
End Namespace
In .NET 9 wurde SearchValues erweitert, sodass die Suche nach Teilzeichenfolgen innerhalb einer größeren Zeichenfolge unterstützt wird. Ein Beispiel finden Sie unter SearchValues Erweiterung.
Methoden, die indirekt einen Zeichenfolgenvergleich durchführen
Einige Nicht-Zeichenfolgenmethoden, die einen Zeichenfolgenvergleich als zentraler Vorgang aufweisen, verwenden den StringComparer Typ. Die StringComparer Klasse enthält sechs statische Eigenschaften, die Instanzen zurückgeben StringComparer , deren StringComparer.Compare Methoden die folgenden Arten von Zeichenfolgenvergleichen ausführen:
- Kulturabhängige Zeichenfolgenvergleiche mit der aktuellen Kultur. Dieses StringComparer Objekt wird von der StringComparer.CurrentCulture Eigenschaft zurückgegeben.
- Vergleiche mit der aktuellen Kultur ohne Beachtung der Groß- und Kleinschreibung. Dieses StringComparer Objekt wird von der StringComparer.CurrentCultureIgnoreCase Eigenschaft zurückgegeben.
- Kulturunempfindliche Vergleiche unter Verwendung der Wortvergleichsregeln der invarianten Kultur. Dieses StringComparer Objekt wird von der StringComparer.InvariantCulture Eigenschaft zurückgegeben.
- Kulturunabhängige Vergleiche mit den Wortvergleichsregeln der invarianten Kultur ohne Beachtung der Groß- und Kleinschreibung. Dieses StringComparer Objekt wird von der StringComparer.InvariantCultureIgnoreCase Eigenschaft zurückgegeben.
- Ordinalvergleich. Dieses StringComparer Objekt wird von der StringComparer.Ordinal Eigenschaft zurückgegeben.
- Ordinalvergleich ohne Beachtung der Groß- und Kleinschreibung. Dieses StringComparer Objekt wird von der StringComparer.OrdinalIgnoreCase Eigenschaft zurückgegeben.
Array.Sort und Array.BinarySearch
Standard-Interpretation: StringComparison.CurrentCulture.
Wenn Sie Daten in einer Sammlung speichern oder gespeicherte Daten aus einer Datei oder Datenbank in eine Sammlung einlesen, können die Invarianten in der Sammlung ungültig werden, wenn die aktuelle Kultur gewechselt wird. Bei der Array.BinarySearch Methode wird davon ausgegangen, dass die zu durchsuchenden Elemente im Array bereits sortiert sind. Um ein beliebiges Zeichenfolgenelement im Array zu sortieren, ruft die Methode die Array.SortString.Compare Methode auf, um einzelne Elemente zu ordnen. Die Verwendung eines kultursensitiven Vergleichs kann gefährlich sein, wenn sich die Kultur zwischen dem Sortieren des Arrays und dem Durchsuchen seines Inhalts ändert. Im folgenden Code beispielsweise wird das Speichern und Abrufen für den Comparer ausgeführt, der implizit von der Thread.CurrentThread.CurrentCulture -Eigenschaft zurückgegeben wird. Wenn sich die Kultur zwischen den Aufrufen von StoreNames und DoesNameExist ändern kann und insbesondere, wenn der Inhalt des Arrays irgendwo zwischen diesen beiden Methodenaufrufen beibehalten wird, kann die binäre Suche fehlschlagen.
// Incorrect
string[] _storedNames;
public void StoreNames(string[] names)
{
_storedNames = new string[names.Length];
// Copy the array contents into a new array
Array.Copy(names, _storedNames, names.Length);
Array.Sort(_storedNames); // Line A
}
public bool DoesNameExist(string name) =>
Array.BinarySearch(_storedNames, name) >= 0; // Line B
' Incorrect
Dim _storedNames As String()
Sub StoreNames(names As String())
ReDim _storedNames(names.Length - 1)
' Copy the array contents into a new array
Array.Copy(names, _storedNames, names.Length)
Array.Sort(_storedNames) ' Line A
End Sub
Function DoesNameExist(name As String) As Boolean
Return Array.BinarySearch(_storedNames, name) >= 0 ' Line B
End Function
Im folgenden Beispiel wird eine empfohlene Variation angezeigt, bei der die gleiche Ordinalvergleichsmethode (kulturunempfindlich) verwendet wird, um das Array zu sortieren und zu durchsuchen. Der Änderungscode wird in den Zeilen, die mit Line A und Line B beschriftet sind, in den beiden Beispielen widergespiegelt.
// Correct
string[] _storedNames;
public void StoreNames(string[] names)
{
_storedNames = new string[names.Length];
// Copy the array contents into a new array
Array.Copy(names, _storedNames, names.Length);
Array.Sort(_storedNames, StringComparer.Ordinal); // Line A
}
public bool DoesNameExist(string name) =>
Array.BinarySearch(_storedNames, name, StringComparer.Ordinal) >= 0; // Line B
' Correct
Dim _storedNames As String()
Sub StoreNames(names As String())
ReDim _storedNames(names.Length - 1)
' Copy the array contents into a new array
Array.Copy(names, _storedNames, names.Length)
Array.Sort(_storedNames, StringComparer.Ordinal) ' Line A
End Sub
Function DoesNameExist(name As String) As Boolean
Return Array.BinarySearch(_storedNames, name, StringComparer.Ordinal) >= 0 ' Line B
End Function
Wenn diese Daten gespeichert und kulturübergreifend verschoben werden und Sortierung verwendet wird, um diese Daten dem Benutzer zu präsentieren, können Sie die Verwendung von StringComparison.InvariantCulture in Betracht ziehen, die sprachlich für eine bessere Benutzerausgabe sorgt und von Änderungen in der Kultur nicht betroffen ist. Im folgenden Beispiel werden die beiden vorherigen Beispiele so geändert, dass die invariante Kultur zum Sortieren und Durchsuchen des Arrays verwendet wird.
// Correct
string[] _storedNames;
public void StoreNames(string[] names)
{
_storedNames = new string[names.Length];
// Copy the array contents into a new array
Array.Copy(names, _storedNames, names.Length);
Array.Sort(_storedNames, StringComparer.InvariantCulture); // Line A
}
public bool DoesNameExist(string name) =>
Array.BinarySearch(_storedNames, name, StringComparer.InvariantCulture) >= 0; // Line B
' Correct
Dim _storedNames As String()
Sub StoreNames(names As String())
ReDim _storedNames(names.Length - 1)
' Copy the array contents into a new array
Array.Copy(names, _storedNames, names.Length)
Array.Sort(_storedNames, StringComparer.InvariantCulture) ' Line A
End Sub
Function DoesNameExist(name As String) As Boolean
Return Array.BinarySearch(_storedNames, name, StringComparer.InvariantCulture) >= 0 ' Line B
End Function
Beispiel für Sammlungen: Hashtable Konstruktor
Hashzeichenfolgen stellen ein zweites Beispiel für einen Vorgang bereit, der von der Art und Weise beeinflusst wird, in der Zeichenfolgen verglichen werden.
Im folgenden Beispiel wird ein Hashtable -Objekt instanziiert, indem das StringComparer -Objekt übergeben wird, das von der StringComparer.OrdinalIgnoreCase -Eigenschaft zurückgegeben wird. Da eine von der StringComparer-Klasse abgeleitete StringComparer-Klasse die IEqualityComparer-Schnittstelle implementiert, wird die GetHashCode-Methode verwendet, um den Hashcode von Zeichenfolgen in der Hashtabelle zu berechnen.
using System.IO;
using System.Collections;
const int InitialCapacity = 100;
Hashtable creationTimeByFile = new(InitialCapacity, StringComparer.OrdinalIgnoreCase);
string directoryToProcess = Directory.GetCurrentDirectory();
// Fill the hash table
PopulateFileTable(directoryToProcess);
// Get some of the files and try to find them with upper cased names
foreach (var file in Directory.GetFiles(directoryToProcess))
PrintCreationTime(file.ToUpper());
void PopulateFileTable(string directory)
{
foreach (string file in Directory.GetFiles(directory))
creationTimeByFile.Add(file, File.GetCreationTime(file));
}
void PrintCreationTime(string targetFile)
{
object? dt = creationTimeByFile[targetFile];
if (dt is DateTime value)
Console.WriteLine($"File {targetFile} was created at time {value}.");
else
Console.WriteLine($"File {targetFile} does not exist.");
}
Imports System.IO
Module Program
Const InitialCapacity As Integer = 100
Private ReadOnly s_creationTimeByFile As New Hashtable(InitialCapacity, StringComparer.OrdinalIgnoreCase)
Private ReadOnly s_directoryToProcess As String = Directory.GetCurrentDirectory()
Sub Main()
' Fill the hash table
PopulateFileTable(s_directoryToProcess)
' Get some of the files and try to find them with upper cased names
For Each File As String In Directory.GetFiles(s_directoryToProcess)
PrintCreationTime(File.ToUpper())
Next
End Sub
Sub PopulateFileTable(directoryPath As String)
For Each file As String In Directory.GetFiles(directoryPath)
s_creationTimeByFile.Add(file, IO.File.GetCreationTime(file))
Next
End Sub
Sub PrintCreationTime(targetFile As String)
Dim dt As Object = s_creationTimeByFile(targetFile)
If TypeOf dt Is Date Then
Console.WriteLine($"File {targetFile} was created at time {DirectCast(dt, Date)}.")
Else
Console.WriteLine($"File {targetFile} does not exist.")
End If
End Sub
End Module
Beispiel für Sammlungen: SortedSet<T> und List<T>.Sort
Dasselbe Lokalisierungsempfindlichkeitsproblem tritt auf, wenn eine sortierte Sammlung von Strings erstellt wird oder wenn eine bestehende stringbasierte Sammlung sortiert wird. Geben Sie immer einen expliziten Vergleich an:
// Words to sort
string[] values = [ "able", "ångström", "apple", "Æble",
"Windows", "Visual Studio" ];
//
// Potentially incorrect code - behavior might vary based on locale.
//
SortedSet<string> mySet = [.. values]; // No comparer specified
List<string> list = [.. values];
list.Sort(); // No comparer specified
//
// Corrected code - uses ordinal sorting; doesn't vary by locale.
//
SortedSet<string> mySet2 = new(values, StringComparer.Ordinal);
List<string> list2 = [.. values];
list2.Sort(StringComparer.Ordinal);
' Words to sort
Dim values As String() = {"able", "ångström", "apple", "Æble",
"Windows", "Visual Studio"}
'
' Potentially incorrect code - behavior might vary based on locale.
'
Dim mySet As New SortedSet(Of String)(values) ' No comparer specified
Dim list As New List(Of String)(values)
list.Sort() ' No comparer specified
'
' Corrected code - uses ordinal sorting; doesn't vary by locale.
'
Dim mySet2 As New SortedSet(Of String)(values, StringComparer.Ordinal)
Dim list2 As New List(Of String)(values)
list2.Sort(StringComparer.Ordinal)
Unterschiede zwischen .NET und .NET Framework
.NET und .NET Framework behandeln Globalisierung unterschiedlich. .NET Framework unter Windows verwendet die National Language Support (NLS) -Einrichtung des Betriebssystems für linguistische Zeichenfolgenvergleiche. .NET verwendet die International Components for Unicode (ICU)-Bibliothek für linguistische Zeichenfolgenvergleiche auf allen unterstützten Plattformen.
Da ICU und NLS unterschiedliche Logik in ihren linguistischen Vergleichen implementieren, können sich die Ergebnisse von Zeichenfolgenmethoden, die kultursensitiven Vergleich verwenden, zwischen .NET und .NET Framework unterscheiden. Dies ist für jede Methode wichtig, die standardmäßig einen sprachlichen Vergleich verwendet, einschließlich:
- String.Compare
- **
String.EndsWith(wenn der erste Parameter ein
stringist) -
String.IndexOf(wenn der erste Parameter ein
stringist) -
String.StartsWith(wenn der erste Parameter ein
stringist) - String.ToLower
- String.ToLowerInvariant
- String.ToUpper
- String.ToUpperInvariant
- System.Globalization.TextInfo (die meisten Mitglieder)
- System.Globalization.CompareInfo (die meisten Mitglieder)
- Array.Sort (beim Sortieren von Arrays von Zeichenfolgen)
- List<T>.Sort() (wenn die Listenelemente Zeichenfolgen sind)
- System.Collections.Generic.SortedDictionary<TKey,TValue> (wenn die Schlüssel Zeichenfolgen sind)
- System.Collections.Generic.SortedList<TKey,TValue> (wenn die Schlüssel Zeichenfolgen sind)
- System.Collections.Generic.SortedSet<T> (wenn der Satz Zeichenfolgen enthält)
Hinweis
Dies ist keine vollständige Liste der betroffenen APIs.
Ein wichtiger Unterschied besteht darin, dass eingebettete Null- und andere Steuerzeichen behandelt werden. Wenn Sie einen linguistischen Vergleich unter NLS verwenden, werden einige Steuerzeichen wie das Nullzeichen (\0) in bestimmten Vergleichskontexten möglicherweise als ignorierbar behandelt. Unter ICU werden diese Zeichen als tatsächliche Zeichen in der Zeichenfolge behandelt. Dies kann dazu führen string.IndexOf(string) , dass unterschiedliche Ergebnisse zurückgegeben werden, wenn die Suchzeichenfolge ein NULL-Zeichen enthält.
Der folgende Code kann beispielsweise abhängig von der aktuellen Laufzeit eine andere Antwort erzeugen:
const string greeting = "Hel\0lo";
Console.WriteLine($"{greeting.IndexOf("\0")}");
// The snippet prints:
//
// '3' when running on .NET Framework and .NET Core 2.x - 3.x (Windows)
// '0' when running on .NET 5 or later (Windows)
// '0' when running on .NET Core 2.x - 3.x or .NET 5 (non-Windows)
// '3' when running on .NET Core 2.x or .NET 5+ (in invariant mode)
Const greeting As String = "Hel" & vbNullChar & "lo"
Console.WriteLine($"{greeting.IndexOf(CStr(vbNullChar))}")
' The snippet prints:
'
' '3' when running on .NET Framework and .NET Core 2.x - 3.x (Windows)
' '0' when running on .NET 5 or later (Windows)
' '0' when running on .NET Core 2.x - 3.x or .NET 5 (non-Windows)
' '3' when running on .NET Core 2.x or .NET 5+ (in invariant mode)
Die beste Möglichkeit, diese plattformübergreifenden und implementierungsübergreifenden Überraschungen zu vermeiden, besteht darin, immer ein explizites StringComparison-Argument an Zeichenfolgenvergleichsmethoden zu übergeben und StringComparison.Ordinal oder StringComparison.OrdinalIgnoreCase für nicht-sprachliche Vergleiche zu verwenden.
Wenn Sie eine Anwendung von .NET Framework zu .NET migrieren und auf ältere NLS-Verhaltensweisen unter Windows angewiesen sind, können Sie die Anwendung so konfigurieren, dass NLS verwendet wird. Weitere Informationen finden Sie unter .NET Globalization und ICU.