1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 using System
.Collections
;
6 using System
.Collections
.Generic
;
7 using System
.Globalization
;
8 using System
.Runtime
.Serialization
;
13 [System
.Runtime
.CompilerServices
.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
14 public abstract class StringComparer
: IComparer
, IEqualityComparer
, IComparer
<string?>, IEqualityComparer
<string?>
16 private static readonly CultureAwareComparer s_invariantCulture
= new CultureAwareComparer(CultureInfo
.InvariantCulture
, CompareOptions
.None
);
17 private static readonly CultureAwareComparer s_invariantCultureIgnoreCase
= new CultureAwareComparer(CultureInfo
.InvariantCulture
, CompareOptions
.IgnoreCase
);
18 private static readonly OrdinalCaseSensitiveComparer s_ordinal
= new OrdinalCaseSensitiveComparer();
19 private static readonly OrdinalIgnoreCaseComparer s_ordinalIgnoreCase
= new OrdinalIgnoreCaseComparer();
21 public static StringComparer InvariantCulture
25 return s_invariantCulture
;
29 public static StringComparer InvariantCultureIgnoreCase
33 return s_invariantCultureIgnoreCase
;
37 public static StringComparer CurrentCulture
41 return new CultureAwareComparer(CultureInfo
.CurrentCulture
, CompareOptions
.None
);
45 public static StringComparer CurrentCultureIgnoreCase
49 return new CultureAwareComparer(CultureInfo
.CurrentCulture
, CompareOptions
.IgnoreCase
);
53 public static StringComparer Ordinal
61 public static StringComparer OrdinalIgnoreCase
65 return s_ordinalIgnoreCase
;
69 // Convert a StringComparison to a StringComparer
70 public static StringComparer
FromComparison(StringComparison comparisonType
)
72 switch (comparisonType
)
74 case StringComparison
.CurrentCulture
:
75 return CurrentCulture
;
76 case StringComparison
.CurrentCultureIgnoreCase
:
77 return CurrentCultureIgnoreCase
;
78 case StringComparison
.InvariantCulture
:
79 return InvariantCulture
;
80 case StringComparison
.InvariantCultureIgnoreCase
:
81 return InvariantCultureIgnoreCase
;
82 case StringComparison
.Ordinal
:
84 case StringComparison
.OrdinalIgnoreCase
:
85 return OrdinalIgnoreCase
;
87 throw new ArgumentException(SR
.NotSupported_StringComparison
, nameof(comparisonType
));
91 public static StringComparer
Create(CultureInfo culture
, bool ignoreCase
)
95 throw new ArgumentNullException(nameof(culture
));
98 return new CultureAwareComparer(culture
, ignoreCase
? CompareOptions
.IgnoreCase
: CompareOptions
.None
);
101 public static StringComparer
Create(CultureInfo culture
, CompareOptions options
)
105 throw new ArgumentException(nameof(culture
));
108 return new CultureAwareComparer(culture
, options
);
111 public int Compare(object? x
, object? y
)
113 if (x
== y
) return 0;
114 if (x
== null) return -1;
115 if (y
== null) return 1;
121 return Compare(sa
, sb
);
125 if (x
is IComparable ia
)
127 return ia
.CompareTo(y
);
130 throw new ArgumentException(SR
.Argument_ImplementIComparable
);
133 public new bool Equals(object? x
, object? y
)
135 if (x
== y
) return true;
136 if (x
== null || y
== null) return false;
142 return Equals(sa
, sb
);
148 public int GetHashCode(object obj
)
152 throw new ArgumentNullException(nameof(obj
));
157 return GetHashCode(s
);
159 return obj
.GetHashCode();
162 public abstract int Compare(string? x
, string? y
);
163 public abstract bool Equals(string? x
, string? y
);
164 #pragma warning disable CS8614 // Remove warning disable when nullable attributes are respected
165 public abstract int GetHashCode(string obj
);
166 #pragma warning restore CS8614
170 [System
.Runtime
.CompilerServices
.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
171 public sealed class CultureAwareComparer
: StringComparer
, ISerializable
173 private const CompareOptions ValidCompareMaskOffFlags
= ~
(CompareOptions
.IgnoreCase
| CompareOptions
.IgnoreSymbols
| CompareOptions
.IgnoreNonSpace
| CompareOptions
.IgnoreWidth
| CompareOptions
.IgnoreKanaType
| CompareOptions
.StringSort
);
175 private readonly CompareInfo _compareInfo
; // Do not rename (binary serialization)
176 private readonly CompareOptions _options
;
178 internal CultureAwareComparer(CultureInfo culture
, CompareOptions options
) : this(culture
.CompareInfo
, options
) { }
180 internal CultureAwareComparer(CompareInfo compareInfo
, CompareOptions options
)
182 _compareInfo
= compareInfo
;
184 if ((options
& ValidCompareMaskOffFlags
) != 0)
186 throw new ArgumentException(SR
.Argument_InvalidFlag
, nameof(options
));
191 private CultureAwareComparer(SerializationInfo info
, StreamingContext context
)
193 _compareInfo
= (CompareInfo
)info
.GetValue("_compareInfo", typeof(CompareInfo
))!;
194 bool ignoreCase
= info
.GetBoolean("_ignoreCase");
196 var obj
= info
.GetValueNoThrow("_options", typeof(CompareOptions
));
198 _options
= (CompareOptions
)obj
;
200 // fix up the _options value in case we are getting old serialized object not having _options
201 _options
|= ignoreCase
? CompareOptions
.IgnoreCase
: CompareOptions
.None
;
204 public override int Compare(string? x
, string? y
)
206 if (object.ReferenceEquals(x
, y
)) return 0;
207 if (x
== null) return -1;
208 if (y
== null) return 1;
209 return _compareInfo
.Compare(x
, y
, _options
);
212 public override bool Equals(string? x
, string? y
)
214 if (object.ReferenceEquals(x
, y
)) return true;
215 if (x
== null || y
== null) return false;
216 return _compareInfo
.Compare(x
, y
, _options
) == 0;
219 public override int GetHashCode(string obj
)
223 throw new ArgumentNullException(nameof(obj
));
225 return _compareInfo
.GetHashCodeOfString(obj
, _options
);
228 // Equals method for the comparer itself.
229 public override bool Equals(object? obj
)
232 obj
is CultureAwareComparer comparer
&&
233 _options
== comparer
._options
&&
234 _compareInfo
.Equals(comparer
._compareInfo
);
237 public override int GetHashCode()
239 return _compareInfo
.GetHashCode() ^
((int)_options
& 0x7FFFFFFF);
242 public void GetObjectData(SerializationInfo info
, StreamingContext context
)
244 info
.AddValue("_compareInfo", _compareInfo
);
245 info
.AddValue("_options", _options
);
246 info
.AddValue("_ignoreCase", (_options
& CompareOptions
.IgnoreCase
) != 0);
251 [System
.Runtime
.CompilerServices
.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
252 public class OrdinalComparer
: StringComparer
254 private readonly bool _ignoreCase
; // Do not rename (binary serialization)
256 internal OrdinalComparer(bool ignoreCase
)
258 _ignoreCase
= ignoreCase
;
261 public override int Compare(string? x
, string? y
)
263 if (ReferenceEquals(x
, y
))
272 return string.Compare(x
, y
, StringComparison
.OrdinalIgnoreCase
);
275 return string.CompareOrdinal(x
, y
);
278 public override bool Equals(string? x
, string? y
)
280 if (ReferenceEquals(x
, y
))
282 if (x
== null || y
== null)
287 if (x
.Length
!= y
.Length
)
291 return CompareInfo
.EqualsOrdinalIgnoreCase(ref x
.GetRawStringData(), ref y
.GetRawStringData(), x
.Length
);
296 public override int GetHashCode(string obj
)
300 ThrowHelper
.ThrowArgumentNullException(ExceptionArgument
.obj
);
305 return obj
.GetHashCodeOrdinalIgnoreCase();
308 return obj
.GetHashCode();
311 // Equals method for the comparer itself.
312 public override bool Equals(object? obj
)
314 if (!(obj
is OrdinalComparer comparer
))
318 return (this._ignoreCase
== comparer
._ignoreCase
);
321 public override int GetHashCode()
323 int hashCode
= nameof(OrdinalComparer
).GetHashCode();
324 return _ignoreCase
? (~hashCode
) : hashCode
;
329 internal sealed class OrdinalCaseSensitiveComparer
: OrdinalComparer
, ISerializable
331 public OrdinalCaseSensitiveComparer() : base(false)
335 public override int Compare(string? x
, string? y
) => string.CompareOrdinal(x
, y
);
337 public override bool Equals(string? x
, string? y
) => string.Equals(x
, y
);
339 public override int GetHashCode(string obj
)
343 ThrowHelper
.ThrowArgumentNullException(ExceptionArgument
.obj
);
345 return obj
.GetHashCode();
348 public void GetObjectData(SerializationInfo info
, StreamingContext context
)
350 info
.SetType(typeof(OrdinalComparer
));
351 info
.AddValue("_ignoreCase", false);
356 internal sealed class OrdinalIgnoreCaseComparer
: OrdinalComparer
, ISerializable
358 public OrdinalIgnoreCaseComparer() : base(true)
362 public override int Compare(string? x
, string? y
) => string.Compare(x
, y
, StringComparison
.OrdinalIgnoreCase
);
364 public override bool Equals(string? x
, string? y
)
366 if (ReferenceEquals(x
, y
))
371 if (x
is null || y
is null)
376 if (x
.Length
!= y
.Length
)
381 return CompareInfo
.EqualsOrdinalIgnoreCase(ref x
.GetRawStringData(), ref y
.GetRawStringData(), x
.Length
);
384 public override int GetHashCode(string obj
)
388 ThrowHelper
.ThrowArgumentNullException(ExceptionArgument
.obj
);
390 return obj
.GetHashCodeOrdinalIgnoreCase();
393 public void GetObjectData(SerializationInfo info
, StreamingContext context
)
395 info
.SetType(typeof(OrdinalComparer
));
396 info
.AddValue("_ignoreCase", true);