3 // Copyright (c) Microsoft Corporation. All rights reserved.
8 using System.Collections;
9 using System.Collections.Generic;
10 using System.Globalization;
11 using System.Diagnostics.Contracts;
12 using System.Runtime.Serialization;
13 using System.Runtime.CompilerServices;
17 [System.Runtime.InteropServices.ComVisible(true)]
18 public abstract class StringComparer : IComparer, IEqualityComparer, IComparer<string>, IEqualityComparer<string>{
19 private static readonly StringComparer _invariantCulture = new CultureAwareComparer(CultureInfo.InvariantCulture, false);
20 private static readonly StringComparer _invariantCultureIgnoreCase = new CultureAwareComparer(CultureInfo.InvariantCulture, true);
21 private static readonly StringComparer _ordinal = new OrdinalComparer(false);
22 private static readonly StringComparer _ordinalIgnoreCase = new OrdinalComparer(true);
24 public static StringComparer InvariantCulture {
26 Contract.Ensures(Contract.Result<StringComparer>() != null);
27 return _invariantCulture;
31 public static StringComparer InvariantCultureIgnoreCase {
33 Contract.Ensures(Contract.Result<StringComparer>() != null);
34 return _invariantCultureIgnoreCase;
38 public static StringComparer CurrentCulture {
40 Contract.Ensures(Contract.Result<StringComparer>() != null);
41 return new CultureAwareComparer(CultureInfo.CurrentCulture, false);
45 public static StringComparer CurrentCultureIgnoreCase {
47 Contract.Ensures(Contract.Result<StringComparer>() != null);
48 return new CultureAwareComparer(CultureInfo.CurrentCulture, true);
52 public static StringComparer Ordinal {
54 Contract.Ensures(Contract.Result<StringComparer>() != null);
59 public static StringComparer OrdinalIgnoreCase {
61 Contract.Ensures(Contract.Result<StringComparer>() != null);
62 return _ordinalIgnoreCase;
66 public static StringComparer Create(CultureInfo culture, bool ignoreCase) {
67 if( culture == null) {
68 throw new ArgumentNullException("culture");
70 Contract.Ensures(Contract.Result<StringComparer>() != null);
71 Contract.EndContractBlock();
73 return new CultureAwareComparer(culture, ignoreCase);
76 public int Compare(object x, object y) {
78 if (x == null) return -1;
79 if (y == null) return 1;
81 String sa = x as String;
83 String sb = y as String;
85 return Compare(sa, sb);
89 IComparable ia = x as IComparable;
91 return ia.CompareTo(y);
94 throw new ArgumentException(Environment.GetResourceString("Argument_ImplementIComparable"));
98 public new bool Equals(Object x, Object y) {
99 if (x == y) return true;
100 if (x == null || y == null) return false;
102 String sa = x as String;
104 String sb = y as String;
106 return Equals(sa, sb);
112 public int GetHashCode(object obj) {
114 throw new ArgumentNullException("obj");
116 Contract.EndContractBlock();
118 string s = obj as string;
120 return GetHashCode(s);
122 return obj.GetHashCode();
125 public abstract int Compare(String x, String y);
126 public abstract bool Equals(String x, String y);
127 public abstract int GetHashCode(string obj);
131 internal sealed class CultureAwareComparer : StringComparer
132 #if FEATURE_RANDOMIZED_STRING_HASHING
133 , IWellKnownStringEqualityComparer
136 private CompareInfo _compareInfo;
137 private bool _ignoreCase;
139 internal CultureAwareComparer(CultureInfo culture, bool ignoreCase) {
140 _compareInfo = culture.CompareInfo;
141 _ignoreCase = ignoreCase;
144 internal CultureAwareComparer(CompareInfo compareInfo, bool ignoreCase) {
145 _compareInfo = compareInfo;
146 _ignoreCase = ignoreCase;
149 public override int Compare(string x, string y) {
150 if (Object.ReferenceEquals(x, y)) return 0;
151 if (x == null) return -1;
152 if (y == null) return 1;
153 return _compareInfo.Compare(x, y, _ignoreCase? CompareOptions.IgnoreCase : CompareOptions.None);
156 public override bool Equals(string x, string y) {
157 if (Object.ReferenceEquals(x ,y)) return true;
158 if (x == null || y == null) return false;
160 return (_compareInfo.Compare(x, y, _ignoreCase? CompareOptions.IgnoreCase : CompareOptions.None) == 0);
163 public override int GetHashCode(string obj) {
165 throw new ArgumentNullException("obj");
167 Contract.EndContractBlock();
169 CompareOptions options = CompareOptions.None;
172 options |= CompareOptions.IgnoreCase;
175 return _compareInfo.GetHashCodeOfString(obj, options);
178 // Equals method for the comparer itself.
179 public override bool Equals(Object obj){
180 CultureAwareComparer comparer = obj as CultureAwareComparer;
181 if( comparer == null) {
184 return (this._ignoreCase == comparer._ignoreCase) && (this._compareInfo.Equals(comparer._compareInfo));
187 public override int GetHashCode() {
188 int hashCode = _compareInfo.GetHashCode() ;
189 return _ignoreCase ? (~hashCode) : hashCode;
192 #if FEATURE_RANDOMIZED_STRING_HASHING
193 IEqualityComparer IWellKnownStringEqualityComparer.GetRandomizedEqualityComparer() {
194 return new CultureAwareRandomizedComparer(_compareInfo, _ignoreCase);
197 IEqualityComparer IWellKnownStringEqualityComparer.GetEqualityComparerForSerialization() {
204 #if FEATURE_RANDOMIZED_STRING_HASHING
205 internal sealed class CultureAwareRandomizedComparer : StringComparer, IWellKnownStringEqualityComparer {
207 private CompareInfo _compareInfo;
208 private bool _ignoreCase;
209 private long _entropy;
211 internal CultureAwareRandomizedComparer(CompareInfo compareInfo, bool ignoreCase) {
212 _compareInfo = compareInfo;
213 _ignoreCase = ignoreCase;
214 _entropy = HashHelpers.GetEntropy();
217 public override int Compare(string x, string y) {
218 if (Object.ReferenceEquals(x, y)) return 0;
219 if (x == null) return -1;
220 if (y == null) return 1;
221 return _compareInfo.Compare(x, y, _ignoreCase? CompareOptions.IgnoreCase : CompareOptions.None);
224 public override bool Equals(string x, string y) {
225 if (Object.ReferenceEquals(x ,y)) return true;
226 if (x == null || y == null) return false;
228 return (_compareInfo.Compare(x, y, _ignoreCase? CompareOptions.IgnoreCase : CompareOptions.None) == 0);
231 public override int GetHashCode(string obj) {
233 throw new ArgumentNullException("obj");
235 Contract.EndContractBlock();
237 CompareOptions options = CompareOptions.None;
240 options |= CompareOptions.IgnoreCase;
243 return _compareInfo.GetHashCodeOfString(obj, options, true, _entropy);
246 // Equals method for the comparer itself.
247 public override bool Equals(Object obj){
248 CultureAwareRandomizedComparer comparer = obj as CultureAwareRandomizedComparer;
249 if( comparer == null) {
252 return (this._ignoreCase == comparer._ignoreCase) && (this._compareInfo.Equals(comparer._compareInfo)) && (this._entropy == comparer._entropy);
255 public override int GetHashCode() {
256 int hashCode = _compareInfo.GetHashCode() ;
257 return ((_ignoreCase ? (~hashCode) : hashCode) ^ ((int) (_entropy & 0x7FFFFFFF)));
260 IEqualityComparer IWellKnownStringEqualityComparer.GetRandomizedEqualityComparer() {
261 return new CultureAwareRandomizedComparer(_compareInfo, _ignoreCase);
264 // We want to serialize the old comparer.
265 IEqualityComparer IWellKnownStringEqualityComparer.GetEqualityComparerForSerialization() {
266 return new CultureAwareComparer(_compareInfo, _ignoreCase);
271 // Provide x more optimal implementation of ordinal comparison.
273 internal sealed class OrdinalComparer : StringComparer
274 #if FEATURE_RANDOMIZED_STRING_HASHING
275 , IWellKnownStringEqualityComparer
278 private bool _ignoreCase;
280 internal OrdinalComparer(bool ignoreCase) {
281 _ignoreCase = ignoreCase;
284 public override int Compare(string x, string y) {
285 if (Object.ReferenceEquals(x, y)) return 0;
286 if (x == null) return -1;
287 if (y == null) return 1;
290 return String.Compare(x, y, StringComparison.OrdinalIgnoreCase);
293 return String.CompareOrdinal(x, y);
296 public override bool Equals(string x, string y) {
297 if (Object.ReferenceEquals(x ,y)) return true;
298 if (x == null || y == null) return false;
301 if( x.Length != y.Length) {
304 return (String.Compare(x, y, StringComparison.OrdinalIgnoreCase) == 0);
309 public override int GetHashCode(string obj) {
311 throw new ArgumentNullException("obj");
313 Contract.EndContractBlock();
316 return TextInfo.GetHashCodeOrdinalIgnoreCase(obj);
319 return obj.GetHashCode();
322 // Equals method for the comparer itself.
323 public override bool Equals(Object obj){
324 OrdinalComparer comparer = obj as OrdinalComparer;
325 if( comparer == null) {
328 return (this._ignoreCase == comparer._ignoreCase);
331 public override int GetHashCode() {
332 string name = "OrdinalComparer";
333 int hashCode = name.GetHashCode();
334 return _ignoreCase ? (~hashCode) : hashCode;
337 #if FEATURE_RANDOMIZED_STRING_HASHING
338 IEqualityComparer IWellKnownStringEqualityComparer.GetRandomizedEqualityComparer() {
339 return new OrdinalRandomizedComparer(_ignoreCase);
342 IEqualityComparer IWellKnownStringEqualityComparer.GetEqualityComparerForSerialization() {
350 #if FEATURE_RANDOMIZED_STRING_HASHING
351 internal sealed class OrdinalRandomizedComparer : StringComparer, IWellKnownStringEqualityComparer {
352 private bool _ignoreCase;
353 private long _entropy;
355 internal OrdinalRandomizedComparer(bool ignoreCase) {
356 _ignoreCase = ignoreCase;
357 _entropy = HashHelpers.GetEntropy();
360 public override int Compare(string x, string y) {
361 if (Object.ReferenceEquals(x, y)) return 0;
362 if (x == null) return -1;
363 if (y == null) return 1;
366 return String.Compare(x, y, StringComparison.OrdinalIgnoreCase);
369 return String.CompareOrdinal(x, y);
372 public override bool Equals(string x, string y) {
373 if (Object.ReferenceEquals(x ,y)) return true;
374 if (x == null || y == null) return false;
377 if( x.Length != y.Length) {
380 return (String.Compare(x, y, StringComparison.OrdinalIgnoreCase) == 0);
385 [System.Security.SecuritySafeCritical]
386 public override int GetHashCode(string obj) {
388 throw new ArgumentNullException("obj");
390 Contract.EndContractBlock();
393 return TextInfo.GetHashCodeOrdinalIgnoreCase(obj, true, _entropy);
396 return String.InternalMarvin32HashString(obj, obj.Length, _entropy);
399 // Equals method for the comparer itself.
400 public override bool Equals(Object obj){
401 OrdinalRandomizedComparer comparer = obj as OrdinalRandomizedComparer;
402 if( comparer == null) {
405 return (this._ignoreCase == comparer._ignoreCase) && (this._entropy == comparer._entropy);
408 public override int GetHashCode() {
409 string name = "OrdinalRandomizedComparer";
410 int hashCode = name.GetHashCode();
411 return ((_ignoreCase ? (~hashCode) : hashCode) ^ ((int) (_entropy & 0x7FFFFFFF)));
414 IEqualityComparer IWellKnownStringEqualityComparer.GetRandomizedEqualityComparer() {
415 return new OrdinalRandomizedComparer(_ignoreCase);
418 // We want to serialize the old comparer.
419 IEqualityComparer IWellKnownStringEqualityComparer.GetEqualityComparerForSerialization() {
420 return new OrdinalComparer(_ignoreCase);
424 // This interface is implemented by string comparers in the framework that can opt into
425 // randomized hashing behaviors.
426 internal interface IWellKnownStringEqualityComparer {
427 // Get an IEqualityComparer that has the same equality comparision rules as "this" but uses Randomized Hashing.
428 IEqualityComparer GetRandomizedEqualityComparer();
429 // Get an IEqaulityComparer that can be serailzied (e.g., it exists in older versions).
430 IEqualityComparer GetEqualityComparerForSerialization();