StringComparer.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  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.
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. using System.Globalization;
  7. using System.Runtime.Serialization;
  8. namespace System
  9. {
  10. [Serializable]
  11. [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
  12. public abstract class StringComparer : IComparer, IEqualityComparer, IComparer<string>, IEqualityComparer<string>
  13. {
  14. private static readonly CultureAwareComparer s_invariantCulture = new CultureAwareComparer(CultureInfo.InvariantCulture, CompareOptions.None);
  15. private static readonly CultureAwareComparer s_invariantCultureIgnoreCase = new CultureAwareComparer(CultureInfo.InvariantCulture, CompareOptions.IgnoreCase);
  16. private static readonly OrdinalCaseSensitiveComparer s_ordinal = new OrdinalCaseSensitiveComparer();
  17. private static readonly OrdinalIgnoreCaseComparer s_ordinalIgnoreCase = new OrdinalIgnoreCaseComparer();
  18. public static StringComparer InvariantCulture
  19. {
  20. get
  21. {
  22. return s_invariantCulture;
  23. }
  24. }
  25. public static StringComparer InvariantCultureIgnoreCase
  26. {
  27. get
  28. {
  29. return s_invariantCultureIgnoreCase;
  30. }
  31. }
  32. public static StringComparer CurrentCulture
  33. {
  34. get
  35. {
  36. return new CultureAwareComparer(CultureInfo.CurrentCulture, CompareOptions.None);
  37. }
  38. }
  39. public static StringComparer CurrentCultureIgnoreCase
  40. {
  41. get
  42. {
  43. return new CultureAwareComparer(CultureInfo.CurrentCulture, CompareOptions.IgnoreCase);
  44. }
  45. }
  46. public static StringComparer Ordinal
  47. {
  48. get
  49. {
  50. return s_ordinal;
  51. }
  52. }
  53. public static StringComparer OrdinalIgnoreCase
  54. {
  55. get
  56. {
  57. return s_ordinalIgnoreCase;
  58. }
  59. }
  60. // Convert a StringComparison to a StringComparer
  61. public static StringComparer FromComparison(StringComparison comparisonType)
  62. {
  63. switch (comparisonType)
  64. {
  65. case StringComparison.CurrentCulture:
  66. return CurrentCulture;
  67. case StringComparison.CurrentCultureIgnoreCase:
  68. return CurrentCultureIgnoreCase;
  69. case StringComparison.InvariantCulture:
  70. return InvariantCulture;
  71. case StringComparison.InvariantCultureIgnoreCase:
  72. return InvariantCultureIgnoreCase;
  73. case StringComparison.Ordinal:
  74. return Ordinal;
  75. case StringComparison.OrdinalIgnoreCase:
  76. return OrdinalIgnoreCase;
  77. default:
  78. throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
  79. }
  80. }
  81. public static StringComparer Create(CultureInfo culture, bool ignoreCase)
  82. {
  83. if (culture == null)
  84. {
  85. throw new ArgumentNullException(nameof(culture));
  86. }
  87. return new CultureAwareComparer(culture, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
  88. }
  89. public static StringComparer Create(CultureInfo culture, CompareOptions options)
  90. {
  91. if (culture == null)
  92. {
  93. throw new ArgumentException(nameof(culture));
  94. }
  95. return new CultureAwareComparer(culture, options);
  96. }
  97. public int Compare(object x, object y)
  98. {
  99. if (x == y) return 0;
  100. if (x == null) return -1;
  101. if (y == null) return 1;
  102. string sa = x as string;
  103. if (sa != null)
  104. {
  105. string sb = y as string;
  106. if (sb != null)
  107. {
  108. return Compare(sa, sb);
  109. }
  110. }
  111. IComparable ia = x as IComparable;
  112. if (ia != null)
  113. {
  114. return ia.CompareTo(y);
  115. }
  116. throw new ArgumentException(SR.Argument_ImplementIComparable);
  117. }
  118. public new bool Equals(object x, object y)
  119. {
  120. if (x == y) return true;
  121. if (x == null || y == null) return false;
  122. string sa = x as string;
  123. if (sa != null)
  124. {
  125. string sb = y as string;
  126. if (sb != null)
  127. {
  128. return Equals(sa, sb);
  129. }
  130. }
  131. return x.Equals(y);
  132. }
  133. public int GetHashCode(object obj)
  134. {
  135. if (obj == null)
  136. {
  137. throw new ArgumentNullException(nameof(obj));
  138. }
  139. string s = obj as string;
  140. if (s != null)
  141. {
  142. return GetHashCode(s);
  143. }
  144. return obj.GetHashCode();
  145. }
  146. public abstract int Compare(string x, string y);
  147. public abstract bool Equals(string x, string y);
  148. public abstract int GetHashCode(string obj);
  149. }
  150. [Serializable]
  151. [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
  152. public sealed class CultureAwareComparer : StringComparer, ISerializable
  153. {
  154. private const CompareOptions ValidCompareMaskOffFlags = ~(CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType | CompareOptions.StringSort);
  155. private readonly CompareInfo _compareInfo; // Do not rename (binary serialization)
  156. private CompareOptions _options;
  157. internal CultureAwareComparer(CultureInfo culture, CompareOptions options) : this(culture.CompareInfo, options) { }
  158. internal CultureAwareComparer(CompareInfo compareInfo, CompareOptions options)
  159. {
  160. _compareInfo = compareInfo;
  161. if ((options & ValidCompareMaskOffFlags) != 0)
  162. {
  163. throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options));
  164. }
  165. _options = options;
  166. }
  167. private CultureAwareComparer(SerializationInfo info, StreamingContext context)
  168. {
  169. _compareInfo = (CompareInfo)info.GetValue("_compareInfo", typeof(CompareInfo));
  170. bool ignoreCase = info.GetBoolean("_ignoreCase");
  171. var obj = info.GetValueNoThrow("_options", typeof(CompareOptions));
  172. if (obj != null)
  173. _options = (CompareOptions)obj;
  174. // fix up the _options value in case we are getting old serialized object not having _options
  175. _options |= ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None;
  176. }
  177. public override int Compare(string x, string y)
  178. {
  179. if (object.ReferenceEquals(x, y)) return 0;
  180. if (x == null) return -1;
  181. if (y == null) return 1;
  182. return _compareInfo.Compare(x, y, _options);
  183. }
  184. public override bool Equals(string x, string y)
  185. {
  186. if (object.ReferenceEquals(x, y)) return true;
  187. if (x == null || y == null) return false;
  188. return _compareInfo.Compare(x, y, _options) == 0;
  189. }
  190. public override int GetHashCode(string obj)
  191. {
  192. if (obj == null)
  193. {
  194. throw new ArgumentNullException(nameof(obj));
  195. }
  196. return _compareInfo.GetHashCodeOfString(obj, _options);
  197. }
  198. // Equals method for the comparer itself.
  199. public override bool Equals(object obj)
  200. {
  201. CultureAwareComparer comparer = obj as CultureAwareComparer;
  202. return
  203. comparer != null &&
  204. _options == comparer._options &&
  205. _compareInfo.Equals(comparer._compareInfo);
  206. }
  207. public override int GetHashCode()
  208. {
  209. return _compareInfo.GetHashCode() ^ ((int)_options & 0x7FFFFFFF);
  210. }
  211. public void GetObjectData(SerializationInfo info, StreamingContext context)
  212. {
  213. info.AddValue("_compareInfo", _compareInfo);
  214. info.AddValue("_options", _options);
  215. info.AddValue("_ignoreCase", (_options & CompareOptions.IgnoreCase) != 0);
  216. }
  217. }
  218. [Serializable]
  219. [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
  220. public class OrdinalComparer : StringComparer
  221. {
  222. private readonly bool _ignoreCase; // Do not rename (binary serialization)
  223. internal OrdinalComparer(bool ignoreCase)
  224. {
  225. _ignoreCase = ignoreCase;
  226. }
  227. public override int Compare(string x, string y)
  228. {
  229. if (ReferenceEquals(x, y))
  230. return 0;
  231. if (x == null)
  232. return -1;
  233. if (y == null)
  234. return 1;
  235. if (_ignoreCase)
  236. {
  237. return string.Compare(x, y, StringComparison.OrdinalIgnoreCase);
  238. }
  239. return string.CompareOrdinal(x, y);
  240. }
  241. public override bool Equals(string x, string y)
  242. {
  243. if (ReferenceEquals(x, y))
  244. return true;
  245. if (x == null || y == null)
  246. return false;
  247. if (_ignoreCase)
  248. {
  249. if (x.Length != y.Length)
  250. {
  251. return false;
  252. }
  253. return CompareInfo.EqualsOrdinalIgnoreCase(ref x.GetRawStringData(), ref y.GetRawStringData(), x.Length);
  254. }
  255. return x.Equals(y);
  256. }
  257. public override int GetHashCode(string obj)
  258. {
  259. if (obj == null)
  260. {
  261. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.obj);
  262. }
  263. if (_ignoreCase)
  264. {
  265. return obj.GetHashCodeOrdinalIgnoreCase();
  266. }
  267. return obj.GetHashCode();
  268. }
  269. // Equals method for the comparer itself.
  270. public override bool Equals(object obj)
  271. {
  272. OrdinalComparer comparer = obj as OrdinalComparer;
  273. if (comparer == null)
  274. {
  275. return false;
  276. }
  277. return (this._ignoreCase == comparer._ignoreCase);
  278. }
  279. public override int GetHashCode()
  280. {
  281. int hashCode = nameof(OrdinalComparer).GetHashCode();
  282. return _ignoreCase ? (~hashCode) : hashCode;
  283. }
  284. }
  285. [Serializable]
  286. internal sealed class OrdinalCaseSensitiveComparer : OrdinalComparer, ISerializable
  287. {
  288. public OrdinalCaseSensitiveComparer() : base(false)
  289. {
  290. }
  291. public override int Compare(string x, string y) => string.CompareOrdinal(x, y);
  292. public override bool Equals(string x, string y) => string.Equals(x, y);
  293. public override int GetHashCode(string obj)
  294. {
  295. if (obj == null)
  296. {
  297. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.obj);
  298. }
  299. return obj.GetHashCode();
  300. }
  301. public void GetObjectData(SerializationInfo info, StreamingContext context)
  302. {
  303. info.SetType(typeof(OrdinalComparer));
  304. info.AddValue("_ignoreCase", false);
  305. }
  306. }
  307. [Serializable]
  308. internal sealed class OrdinalIgnoreCaseComparer : OrdinalComparer, ISerializable
  309. {
  310. public OrdinalIgnoreCaseComparer() : base(true)
  311. {
  312. }
  313. public override int Compare(string x, string y) => string.Compare(x, y, StringComparison.OrdinalIgnoreCase);
  314. public override bool Equals(string x, string y)
  315. {
  316. if (ReferenceEquals(x, y))
  317. {
  318. return true;
  319. }
  320. if (x is null || y is null)
  321. {
  322. return false;
  323. }
  324. if (x.Length != y.Length)
  325. {
  326. return false;
  327. }
  328. return CompareInfo.EqualsOrdinalIgnoreCase(ref x.GetRawStringData(), ref y.GetRawStringData(), x.Length);
  329. }
  330. public override int GetHashCode(string obj)
  331. {
  332. if (obj == null)
  333. {
  334. ThrowHelper.ThrowArgumentNullException(ExceptionArgument.obj);
  335. }
  336. return obj.GetHashCodeOrdinalIgnoreCase();
  337. }
  338. public void GetObjectData(SerializationInfo info, StreamingContext context)
  339. {
  340. info.SetType(typeof(OrdinalComparer));
  341. info.AddValue("_ignoreCase", true);
  342. }
  343. }
  344. }