Bewährte Methoden zum Vergleichen von Zeichenfolgen in .NET
.NET bietet umfangreiche Unterstützung für das Entwickeln von lokalisierten und globalisierten Anwendungen und erleichtert bei der Ausführung allgemeiner Operationen das Übernehmen von Konventionen der aktuellen oder einer anderen Kultur, beispielsweise bei der Sortierung und Anzeige von Zeichenfolgen. Das Sortieren oder Vergleichen von Zeichenfolgen ist jedoch nicht immer eine kulturabhängige Operation. Beispielsweise sollten interne Zeichenfolgen von Anwendungen i. d. R. in allen Kulturen gleich behandelt werden. Wenn kulturabhängige Zeichenfolgendaten, z. B. XML-Tags, HTML-Tags, Benutzernamen, Dateipfade und Systemobjektnamen, kulturabhängig interpretiert werden, können Fehler im Anwendungscode auftreten, die Leistung kann sich verschlechtern, und in einigen Fällen kann es zu Sicherheitsproblemen kommen.
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 zur Zeichenfolgenverwendung
Beachten Sie bei der Entwicklung mit .NET folgende Empfehlungen zum Vergleichen von Zeichenfolgen.
Tipp
Verschiedene auf Zeichenfolgen bezogene Methoden führen einen Vergleich durch. Beispiele hierfür sind String.Equals, String.Compare, String.IndexOf und String.StartsWith.
- Verwenden Sie Überladungen, die die Regeln für Zeichenfolgenvergleiche in Zeichenfolgenoperationen explizit angeben. Normalerweise müssen Sie dazu eine Methodenüberladung mit einem Parameter vom Typ StringComparisonaufrufen.
- 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, um die Ausgabe für Benutzer anzuzeigen.
- Verwenden Sie den nicht linguistischen StringComparison.Ordinal -Wert oder den nicht linguistischen StringComparison.OrdinalIgnoreCase -Wert anstelle von Zeichenfolgenoperationen, die auf CultureInfo.InvariantCulture basieren, wenn der Vergleich linguistisch nicht relevant (z. B. symbolisch) ist.
- Verwenden Sie die String.ToUpperInvariant -Methode anstelle der String.ToLowerInvariant -Methode, wenn Sie Zeichenfolgen für einen Vergleich normalisieren.
- Verwenden Sie eine Überladung der String.Equals -Methode, um zu überprüfen, ob zwei Zeichenfolgen übereinstimmen.
- Verwenden Sie die String.Compare - und String.CompareTo -Methoden zum Sortieren von Zeichenfolgen, und nicht, um eine Überprüfung auf Gleichheit durchzuführen.
- Verwenden Sie kulturabhängige Formatierung, um Daten, die keine Zeichenfolge sind, z. B. Zahlen und Datumsangaben, auf einer Benutzeroberfläche anzuzeigen. Verwenden Sie Formatierung mit der invarianten Kultur, um für Daten, die keine Zeichenfolge sind, das Zeichenfolgenformat beizubehalten.
Vermeiden Sie beim Vergleichen von Zeichenfolgen folgende Vorgehensweisen:
- Verwenden Sie keine Überladungen, die die Regeln für Zeichenfolgenvergleiche in Zeichenfolgenoperationen nicht explizit oder implizit angeben.
- Verwenden Sie keine Zeichenfolgenoperationen, die auf StringComparison.InvariantCulture basieren. Eine der wenigen Ausnahmen stellt dabei das Persistieren von Zeichenfolgen dar, die zwar linguistisch relevant, jedoch kulturunabhängig sind.
- Verwenden Sie keine Überladung der String.Compare-Methode oder der CompareTo-Methode, und testen Sie auf einen Rückgabewert von null, um zu ermitteln, ob zwei Zeichenfolgen übereinstimmen.
Explizites Angeben von Zeichenfolgenvergleichen
Die Methoden zum Bearbeiten von Zeichenfolgen in .NET werden i.d.R. überladen. Während einige Überladungen normalerweise Standardwerte akzeptieren, geben andere Überladungen exakt an, wie 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 Member der StringComparison -Enumeration beschrieben.
StringComparison-Member | 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 durch. |
OrdinalIgnoreCase | Führt einen Ordinalvergleich ohne Beachtung der Groß- und Kleinschreibung durch. |
Die IndexOf -Methode, die den Index einer Teilzeichenfolge in einem String -Objekt zurückgibt, das einem Zeichen oder einer Zeichenfolge entspricht, weist beispielsweise 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)enthalten einen Parameter vom Typ StringComparison , mit dem das Format des Vergleichs angegeben werden kann.
Aus den folgenden Gründen wird empfohlen, eine Überladung ohne Standardwerte auszuwählen:
Einige Überladungen mit Standardparametern (die in der Zeichenfolgeninstanz nach einem Char suchen) führen einen Ordinalvergleich durch, während andere Überladungen (die in der Zeichenfolgeninstanz nach einer Zeichenfolge suchen) kulturabhängig sind. Es ist nicht leicht, sich zu merken, welche Methode welchen Standardwert verwendet, und Überladungen können schnell verwechselt werden.
Der Zweck des Codes, der Standardwerte für Methodenaufrufe verwendet, ist nicht klar. Im folgenden Beispiel, das auf Standardwerten beruht, ist nur schwer erkennbar, ob tatsächlich ein Ordinalvergleich oder ein linguistischer Vergleich von zwei Zeichenfolgen durchgeführt werden sollte oder ob ein Unterschied in der Groß-/Kleinschreibung zwischen
url.Scheme
und „https“ möglicherweise dazu führt, dass bei einer Prüfung auf Gleichheitfalse
zurückgegeben wird.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 empfiehlt es sich, eine Methode aufzurufen, die keine Standardwerte verwendet, da der Zweck des Codes andernfalls möglicherweise nicht eindeutig ist. Dies erleichtert wiederum das Lesen, Verwalten und Debuggen des Codes. Im folgenden Beispiel wird auf die Fragen eingegangen, die sich zum vorherigen Beispiel stellen. Es wird deutlich gemacht, dass der Ordinalvergleich verwendet wird und dass Unterschiede in der Groß- und Kleinschreibung 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 zum Zeichenfolgenvergleich
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 wird beim Vergleich implizit Gleichheit definiert. Die Vergleichsoperation gibt 0 (null) für Zeichenfolgen zurück, die als gleich betrachtet werden. Dies kann so interpretiert werden, dass keine Zeichenfolge kleiner ist als die andere. Sinnvolle Operationen mit Zeichenfolgen schließen i. d. R. mindestens eines der folgenden Verfahren ein: Vergleich mit einer anderen Zeichenfolge und Ausführen eines genau definierten Sortiervorgangs.
Hinweis
Sie können die aus mehreren Textdateien bestehenden Sorting Weight Tables herunterladen. Diese Textdateien enthalten Informationen zur Gewichtung der Zeichen, die bei Sortier- und Vergleichsvorgängen unter Windows-Betriebssystemen verwendet werden, sowie die Default Unicode Collation Element Table, die neueste Version der Sortiergewichtungstabelle für Linux und macOS. Die spezifische Version der Sortiergewichtungstabelle unter Linux und macOS hängt von der auf dem System installierten Version der International Components for Unicode ab. Informationen zu ICU-Versionen und den Unicode-Versionen, die durch sie implementiert werden, finden Sie unter Downloading ICU.
Die Auswertung zweier Zeichenfolgen auf Gleichheit oder auf ihre Sortierreihenfolge ergibt jedoch nicht ein einziges richtiges Ergebnis. Das Ergebnis ist vielmehr abhängig von den Kriterien, die dem Zeichenfolgenvergleich zugrunde liegen. 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.
Ferner können Zeichenfolgenvergleiche, die verschiedene .NET-Versionen nutzen oder .NET unter verschiedenen Betriebssystemen oder Betriebssystemversionen verwenden, unterschiedliche Ergebnisse zurückgeben. Weitere Informationen finden Sie unter Zeichenfolgen und der Unicode-Standard.
Zeichenfolgenvergleiche mit der aktuellen Kultur
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. Vergleiche, die auf der aktuellen Kultur basieren, sollten immer dann verwendet werden, wenn Daten linguistisch relevant sind und kulturabhängige Interaktionen von Benutzern widerspiegeln.
Das Verhalten im Hinblick auf Vergleiche und die Groß- und Kleinschreibung in .NET ändert sich jedoch, wenn sich die Kultur ändert. Dies ist der Fall, wenn eine Anwendung auf einem Computer mit einer anderen Kultur ausgeführt wird, als der Computer, auf dem die Anwendung entwickelt wurde, oder wenn der ausführende Thread seine Kultur ändert. Dieses Verhalten ist beabsichtigt, für viele Entwickler jedoch nicht offensichtlich. Im folgenden Beispiel werden die Unterschiede zwischen den Sortierreihenfolgen in der englischen (amerikanisch, "en-US") und schwedischen ("sv-SE") Kultur herausgestellt. Beachten Sie, dass die Wörter „ångström“, „Windows“ und „Visual Studio“ in den sortierten Zeichenfolgenarrays an unterschiedlichen Positionen 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 zeigt sich möglicherweise auch bei Sortierreihenfolgen.
Vergleiche mit der Semantik der aktuellen Kultur werden standardmäßig für folgende Methoden verwendet:
- String.Compare-Überladungen, die keinen StringComparison-Parameter enthalten.
- String.CompareTo -Überladungen.
- Die String.StartsWith(String) -Standardmethode und die String.StartsWith(String, Boolean, CultureInfo) -Methode mit einem
null
CultureInfo -Parameter enthalten. - Die String.EndsWith(String)-Standardmethode und die String.EndsWith(String, Boolean, CultureInfo)-Methode mit einem
null
CultureInfo-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.
Wenn nicht linguistische Zeichenfolgendaten linguistisch interpretiert werden, oder wenn Zeichenfolgendaten in einer bestimmten Kultur mit den Konventionen einer anderen Kultur interpretiert werden, können offensichtliche und nur schwer erkennbare Fehler auftreten. Kanonisches Beispiel ist das Problem mit dem Zeichen "I" im Türkischen.
In beinahe allen lateinischen Alphabetarten, einschließlich des englischen (USA) Alphabets, ist das Zeichen "i" (\u0069) die Kleinschreibungsvariante 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 ("İ" bzw. \u0130), welches den Großbuchstaben des Zeichens "i" darstellt. Ferner verfügt Türkisch auch über ein kleines „i ohne Punkt“ („ı“ bzw. \u0131), dessen Entsprechung als Großbuchstabe das Zeichen „I“ ist. Die Kultur Aserbaidschanisch (az-AZ) weist ein analoges Verhalten auf.
Annahmen über die Großschreibung von „i“ oder die Kleinschreibung von „I“ sind daher nicht für alle Kulturen gleichermaßen gültig. 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 signifikante Probleme verursachen, wenn die Kultur versehentlich in sicherheitsrelevanten Einstellungen verwendet wird, wie im folgenden Beispiel veranschaulicht. Ein Methodenaufruf wie IsFileURI("file:")
gibt true
zurück, wenn die aktuelle Kultur "Englisch (USA)" ist und 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
Stattdessen sollte der Code daher in diesem Fall wie im folgenden Beispiel geschrieben werden, da „file:“ als nicht linguistischer und kulturunabhängiger Bezeichner interpretiert werden soll:
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
Ordinalzeichenfolgenvorgänge
Der StringComparison.Ordinal -Wert oder der StringComparison.OrdinalIgnoreCase -Wert in einem Methodenaufruf kennzeichnen einen nicht linguistischen Vergleich, in dem die Funktionen von natürlichen 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 aller Regel ist diese Vorgehensweise am besten für die beabsichtigte Interpretation von Zeichenfolgen geeignet und macht den Code sicherer und zuverlässiger.
Ordinalvergleiche sind Zeichenfolgenvergleiche, in denen die einzelnen Bytes einer Zeichenfolge ohne linguistische Interpretation verglichen werden, z. B. entspricht „windows“ nicht „Windows“. Dies stellt im Grunde einen Aufruf der strcmp
-Funktion dar. Verwenden Sie diesen Vergleich, wenn der Kontext eine exakte Entsprechung von Zeichenfolgen oder eine konservative Entsprechungsrichtlinie verlangt. Darüber hinaus werden Ordinalvergleiche am schnellsten durchgeführt, da beim Bestimmen eines Ergebnisses keine linguistischen Regeln verwendet werden.
Zeichenfolgen in .NET können eingebettete NULL-Zeichen (und andere nicht druckbare Zeichen) enthalten. Einer der auffälligsten Unterschiede zwischen einem ordinalen und einem kulturabhängigen Vergleich (einschließlich Vergleichen, die die invariante Kultur verwenden) betrifft die Behandlung von eingebetteten NULL-Zeichen in einer Zeichenfolge. Wenn Sie die String.Compare -Methode und die String.Equals -Methode verwenden, um kulturabhängige Vergleiche (einschließlich Vergleichen, die die invariante Kultur verwenden) durchzuführen, werden diese Zeichen ignoriert. Zeichenfolgen mit eingebetteten NULL-Zeichen können daher als gleichwertig mit Zeichenfolgen ohne diese Zeichen angesehen werden. Eingebettete nicht druckbare Zeichen können für Methoden zum Vergleich von Zeichenfolgen wie String.StartsWithübersprungen werden.
Wichtig
Auch wenn eingebettete NULL-Zeichen von Zeichenfolgenvergleichsmethoden ignoriert werden, ist dies bei Zeichenfolgensuchmethoden wie String.Contains, String.EndsWith, String.IndexOf, String.LastIndexOfund String.StartsWith nicht der Fall.
Im folgenden Beispiel wird ein kulturabhängiger Vergleich der Zeichenfolge „Aa“ mit einer ähnlichen Zeichenfolge durchgeführt, die mehrere eingebettete NULL-Zeichen zwischen „A“ und „a“ enthält, und es wird angezeigt, inwieweit die beiden Zeichenfolgen als gleich betrachtet 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
Wie das folgende Beispiel zeigt, werden die Zeichenfolgen bei einem Ordinalvergleich jedoch nicht als gleich betrachtet:
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. Ein beliebiges Zeichen in [A, Z] (\u0041-\u005A) entspricht daher dem zugehörigen Zeichen in [a,z] (\u0061-\007A). Die Groß- und Kleinschreibung außerhalb des ASCII-Bereichs verwendet die Tabellen der invarianten Kultur. Daher entspricht der folgende Vergleich
string.Compare(strA, strB, StringComparison.OrdinalIgnoreCase);
String.Compare(strA, strB, StringComparison.OrdinalIgnoreCase)
dem nachstehenden Vergleich (ist jedoch schneller):
string.Compare(strA.ToUpperInvariant(), strB.ToUpperInvariant(), StringComparison.Ordinal);
String.Compare(strA.ToUpperInvariant(), strB.ToUpperInvariant(), StringComparison.Ordinal)
Diese Vergleiche werden immer noch sehr schnell durchgeführt.
StringComparison.Ordinal und StringComparison.OrdinalIgnoreCase verwenden die Binärwerte direkt und eignen sich am besten für Vergleiche. Wenn Sie im Hinblick auf Ihre Vergleichseinstellungen nicht sicher sind, verwenden Sie einen dieser beiden Werte. Da hier jedoch ein Vergleich auf Byteebene durchgeführt wird, erfolgt die Sortierung nicht anhand einer linguistischen Sortierreihenfolge (wie in einem Wörterbuch), sondern anhand einer binären Sortierreihenfolge. Die Ergebnisse erscheinen Benutzern möglicherweise in den meisten Kontexten seltsam.
Ordinalsemantik ist die Standardeinstellung für String.Equals-Überladungen, die kein StringComparison-Argument (einschließlich des Gleichheitsoperators) enthalten. In jedem Fall wird empfohlen, eine Überladung mit einem StringComparison -Parameter aufzurufen.
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 in allen Systemen gleich: Zeichen außerhalb des Bereichs werden in Zeichen übersetzt, von denen angenommen wird, dass es sich um die invarianten Entsprechungen handelt. 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 Unterschiede bezüglich der Groß- und Kleinschreibung in den übersetzten Zeichen werden ignoriert.
Vergleiche, die StringComparison.InvariantCulture verwenden, und StringComparison.Ordinal , werden analog für ASCII-Zeichenfolgen ausgeführt. StringComparison.InvariantCulture trifft jedoch linguistische Entscheidungen, die sich möglicherweise nicht für Zeichenfolgen eignen, die als Bytesatz interpretiert werden müssen. Das CultureInfo.InvariantCulture.CompareInfo
-Objekt führt dazu, dass die Compare -Methode bestimmte Zeichensätze als äquivalent interpretiert. Die folgende Äquivalenz ist beispielsweise in 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 das folgende Beispiel zeigt, unterscheidet sich dieses Verhalten vom Ordinalvergleich.
string separated = "\u0061\u030a";
string combined = "\u00e5";
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
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
Wenn sie Dateinamen, Cookies oder andere Elemente interpretieren, die eine Kombination wie „å“ enthalten können, bieten Ordinalvergleiche immer noch das transparenteste und am besten geeignete Verhalten an.
Insgesamt verfügt die invariante Kultur über wenige Eigenschaften, die für einen Vergleich hilfreich sind. Sie führt Vergleiche mit linguistischer Relevanz durch und kann daher keine vollständige symbolische Äquivalenz garantieren, eignet sich jedoch nicht unbedingt für die Anzeige in einer beliebigen Kultur. Einer der wenigen Gründe, StringComparison.InvariantCulture für Vergleiche zu verwenden, besteht darin, sortierte Daten für die kulturübergreifend einheitliche Anzeige beizubehalten. Wenn beispielsweise eine große Datendatei mit einer Liste sortierter Anzeigebezeichner einer Anwendung angehört, würde das Hinzufügen von Elementen zu dieser Liste einen Einfügevorgang mit invarianter Sortierung erfordern.
Auswählen eines StringComparison-Members für den Methodenaufruf
In der folgenden Tabelle wird die Zuordnung von semantischem Zeichenfolgenkontext zu einem StringComparison-Enumerationsmember beschrieben:
Daten | Verhalten | System.StringComparison-Entsprechung value |
---|---|---|
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 linguistischer Bezeichner mit exakt übereinstimmenden Bytes. | 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 linguistischer Bezeichner, wo die Groß-/Kleinschreibung irrelevant ist. | OrdinalIgnoreCase |
Einige beibehaltene, linguistisch relevante Daten. Anzeige von linguistischen Daten, die eine feste Sortierreihenfolge erfordern. |
Kulturunabhängige Daten, die dennoch linguistisch relevant sind. | InvariantCulture - oder - InvariantCultureIgnoreCase |
Daten, die dem Benutzer angezeigt werden. Die meisten Benutzereingaben. |
Daten, die lokale linguistische Regeln erfordern. | CurrentCulture - oder - CurrentCultureIgnoreCase |
Allgemeine Methoden zum Zeichenfolgenvergleich in .NET
In den folgenden Abschnitten werden die Methoden beschrieben, die am häufigsten für Zeichenfolgenvergleiche verwendet werden.
String.Compare
Standardinterpretation: StringComparison.CurrentCulture.
Der Aufruf dieser Methode stellt die zentrale Operation für Zeichenfolgeninterpretation dar. Aus diesem Grund sollten alle Instanzen von Methodenaufrufen untersucht werden, um zu bestimmen, ob Zeichenfolgen anhand der aktuellen Kultur oder unabhängig von der Kultur (symbolisch) interpretiert werden sollen. Üblicherweise handelt es sich um eine symbolische Interpretation, und es sollte stattdessen 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 zahlreiche Vergleichsoptionen (ordinal, ohne Leerraumzeichen, ohne Zeichen vom Typ Kana usw.) über die CompareOptions -Flagenumeration zur Verfügung stellt.
String.CompareTo
Standardinterpretation: StringComparison.CurrentCulture.
Diese Methode bietet derzeit keine Überladung an, die einen StringComparison-Typ angibt. Diese Methode kann normalerweise in das empfohlene String.Compare(String, String, StringComparison)-Format konvertiert werden.
Diese Methode wird von Typen implementiert, die die IComparable - und die IComparable<T> -Schnittstelle implementieren. Da der StringComparison-Parameter hier nicht verfügbar ist, ermöglichen implementierende Typen oftmals die Angabe eines StringComparer im entsprechenden Konstruktor. 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
Standardinterpretation: 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 . Unabhängig davon wird jedoch empfohlen, eine Überladung aufrufen, die den StringComparison -Typ explizit angibt; dies gilt auch, wenn Sie einen Ordinalvergleich ausführen möchten. Auf diese Weise können bestimmte Zeichenfolgeninterpretationen leichter in Code gefunden werden.
String.ToUpper und String.ToLower
Standardinterpretation: 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 -Methode und die String.ToLowerInvariant -Methode 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
Standardinterpretation: StringComparison.CurrentCulture.
Die Methoden Char.ToUpper(Char) und Char.ToLower(Char) funktionieren ähnlich wie die Methoden String.ToUpper() und String.ToLower(), die im vorherigen Abschnitt beschrieben werden.
String.StartsWith und String.EndsWith
Standardinterpretation: StringComparison.CurrentCulture.
Standardmäßig führen beide Methoden einen kulturabhängigen Vergleich durch. Insbesondere können sie nicht druckbare Zeichen ignorieren.
String.IndexOf und String.LastIndexOf
Standardinterpretation: StringComparison.CurrentCulture.
Die Durchführung von Vergleichen durch die Standardüberladungen dieser Methoden ist nicht konsistent. Alle String.IndexOf - und String.LastIndexOf -Methoden mit einem Char -Parameter führen einen Ordinalvergleich durch. Die String.IndexOf - und die String.LastIndexOf -Standardmethode mit einem String -Parameter führen dagegen einen kulturabhängigen Vergleich durch.
Wenn Sie die String.IndexOf(String) -Methode oder die String.LastIndexOf(String) -Methode aufrufen und eine Zeichenfolge übergeben, die in der aktuellen Instanz gesucht werden soll, empfiehlt es sich, eine Überladung aufrufen, die den StringComparison -Typ explizit angibt. Mit den Überladungen, die ein Char-Argument enthalten, können Sie keinen StringComparison-Typ angeben.
Methoden für den indirekten Zeichenfolgenvergleich
Einige Methoden, die keine Zeichenfolgenmethoden darstellen und deren zentrale Operation ein Zeichenfolgenvergleich ist, verwenden den StringComparer -Typ. Die StringComparer -Klasse enthält sechs statische Eigenschaften, die StringComparer -Instanzen zurückgeben, deren StringComparer.Compare -Methoden folgende Arten von Zeichenfolgenvergleichen durchfü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.
- Kulturunabhängige Vergleiche mit den 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
Standardinterpretation: StringComparison.CurrentCulture.
Wenn Sie Daten in einer Auflistung speichern oder beibehaltene Daten aus einer Datei oder einer Datenbank in eine Auflistung lesen, können die Invarianten in der Auflistung durch einen Wechsel der aktuellen Kultur ungültig werden. Die Array.BinarySearch -Methode geht davon aus, dass die Elemente im zu durchsuchenden Array bereits sortiert wurden. Um die einzelnen Zeichenfolgenelemente im Array zu sortieren, ruft die Array.Sort -Methode die String.Compare -Methode auf. Kulturabhängige Vergleich bergen ein gewisses Risiko, falls sich die Kultur zwischen dem Zeitpunkt der Sortierung des Arrays und dem Durchsuchen des 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 dem Aufruf von StoreNames
und dem Aufruf von DoesNameExist
möglicherweise ändert, kann bei der binären Suche ein Fehler auftreten; dies gilt insbesondere, wenn der Arrayinhalt zwischen den beiden Methodenaufrufen beibehalten wird.
// 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
Eine empfohlene Variante wird im folgenden Beispiel angezeigt, in dem die gleiche (kulturunabhängige) Ordinalvergleichsmethode verwendet wird, um das Array zu sortieren und zu durchsuchen. Der Änderungscode wird in den zwei Beispielen in den Zeilen mit der Bezeichnung Line A
und Line B
angezeigt.
// 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 beibehalten und zwischen Kulturen verschoben werden, und wenn eine Sortierung verwendet wird, um die Daten für den Benutzer anzuzeigen, können Sie StringComparison.InvariantCultureverwenden, da durch die linguistische Verarbeitung eine bessere Benutzerausgabe erzielt wird und Änderungen der Kultur keine Auswirkungen haben. Im folgenden Beispiel werden die zwei vorherigen Beispiele geändert, um die invariante Kultur zum Sortieren und Durchsuchen des Arrays zu verwenden.
// 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 einer Sammlung: Hashtable-Konstruktor
Auch beim Hashen von Zeichenfolgen können sich, je nachdem, wie die Zeichenfolgen verglichen werden, Auswirkungen auf die Operation ergeben.
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 Klasse StringComparer , die von StringComparer abgeleitet wird, 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