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
_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())
Return _comparer.Compare(Name, DirectCast(obj, FileName).Name)
End If
End Function
End Class
String.Equals
默认解释:
StringComparison.Ordinal
。
String
类可通过调用静态或实例
Equals
方法重载或使用静态相等运算符,测试是否相等。 默认情况下,重载和运算符使用序号比较。 但是,我们仍然建议调用显式指定
StringComparison
类型的重载,即使想要执行序号比较;这将更轻松地搜索特定字符串解释的代码。
String.ToUpper 和 String.ToLower
默认解释:
StringComparison.CurrentCulture
。
应谨慎使用
String.ToUpper()
和
String.ToLower()
方法,因为将字符串强制为大写或小写经常用作在不考虑大小写的情况下比较字符串的较小规范化。 如果是这样,请考虑使用不区分大小写的比较。
还可以使用
String.ToUpperInvariant
和
String.ToLowerInvariant
方法。
ToUpperInvariant
是规范化大小写的标准方式。 使用
StringComparison.OrdinalIgnoreCase
进行的比较在行为上是两个调用的组合:对两个字符串参数调用
ToUpperInvariant
,并使用
StringComparison.Ordinal
执行比较。
通过向方法传递表示区域性的
CultureInfo
对象,重载也已可用于转换该特性区域性中的大写和小写字母。
Char.ToUpper 和 Char.ToLower
默认解释:
StringComparison.CurrentCulture
。
Char.ToUpper(Char)
和
Char.ToLower(Char)
方法的工作原理类似于上一节中所述的
String.ToUpper()
和
String.ToLower()
方法。
String.StartsWith 和 String.EndsWith
默认解释:
StringComparison.CurrentCulture
。
默认情况下,这两种方法执行区分区域性的比较。 具体而言,它们可能会忽略非打印字符。
String.IndexOf 和 String.LastIndexOf
默认解释:
StringComparison.CurrentCulture
。
这些方法的默认重载如何执行比较方面缺乏一致性。 包含
String.IndexOf
参数的所有
String.LastIndexOf
和
Char
方法都执行序号比较,但是包含
String.IndexOf
参数的默认
String.LastIndexOf
和
String
方法都执行区分区域性的比较。
如果调用
String.IndexOf(String)
或
String.LastIndexOf(String)
方法并向其传递一个字符串以在当前实例中查找,那么我们建议调用显式指定
StringComparison
类型的重载。 包括
Char
参数的重载不允许指定
StringComparison
类型。
将字符串比较作为核心操作的一些非字符串方法使用
StringComparer
类型。
StringComparer
类型包含六个返回
StringComparer
实例的静态属性,这些实例的
StringComparer.Compare
方法可执行以下类型的字符串比较:
使用当前区域性的区分区域性的字符串比较。 此
StringComparer
对象由
StringComparer.CurrentCulture
属性返回。
使用当前区域性的不区分区域性的比较。 此
StringComparer
对象由
StringComparer.CurrentCultureIgnoreCase
属性返回。
使用固定区域性的单词比较规则的不区分区域性的比较。 此
StringComparer
对象由
StringComparer.InvariantCulture
属性返回。
使用固定区域性的单词比较规则的不区分大小写和不区分区域性的比较。 此
StringComparer
对象由
StringComparer.InvariantCultureIgnoreCase
属性返回。
序号比较。 此
StringComparer
对象由
StringComparer.Ordinal
属性返回。
不区分大小写的序号比较。 此
StringComparer
对象由
StringComparer.OrdinalIgnoreCase
属性返回。
Array.Sort 和 Array.BinarySearch
默认解释:
StringComparison.CurrentCulture
。
当在集合中存储任何数据,或将持久数据从文件或数据库中读取到集合中时,切换当前区域性可能会使集合中的固定条件无效。
Array.BinarySearch
方法假定已对数组中要搜索的元素排序。 若要对数组中的任何字符串元素进行排序,
Array.Sort
方法会调用
String.Compare
方法以对各个元素进行排序。 如果对数组进行排序和搜索其内容的时间范围内区域性发生变化,那么使用区分区域性的比较器会很危险。 例如在下面的代码中,是在由
Thread.CurrentThread.CurrentCulture
属性。 如果在调用
StoreNames
和
DoesNameExist
之间更改了区域性(尤其是数组内容保存在两个方法调用之间的某个位置),那么二进制搜索可能会失败。
// 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
建议的变体将显示在下面使用相同序号(不区分区域性)比较方法进行排序并搜索数组的示例中。 在这两个示例中,更改代码会反映在标记
Line A
和
Line B
的代码行中。
// 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
如果此数据永久保留并跨区域性移动,并且使用排序来向用户显示此数据,则可以考虑使用
StringComparison.InvariantCulture
,其语言操作可获得更好的用户输出且不受区域性更改的影响。 下面的示例修改了前面两个示例,使用固定区域性对数组进行排序和搜索。
// 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
集合示例:哈希表构造函数
哈希字符串提供了第二个运算示例,该运算受比较字符串的方式影响。
下面的示例实例化
Hashtable
对象,方法是向其传递由
StringComparer
属性返回的
StringComparer.OrdinalIgnoreCase
对象。 由于派生自
StringComparer
的类
StringComparer
实现
IEqualityComparer
接口,其
GetHashCode
方法用于计算哈希表中的字符串的哈希代码。
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}.");
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())
End Sub
Sub PopulateFileTable(directoryPath As String)
For Each file As String In Directory.GetFiles(directoryPath)
s_creationTimeByFile.Add(file, IO.File.GetCreationTime(file))
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)}.")
Console.WriteLine($"File {targetFile} does not exist.")
End If
End Sub
End Module
.NET 应用中的全球化