PageRenderTime 39ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/Dynamite/ExtendedComparer.cs

#
C# | 363 lines | 256 code | 44 blank | 63 comment | 41 complexity | 1f73c4606fd2908573bb9c24890c666c MD5 | raw file
Possible License(s): Apache-2.0
  1. /*
  2. * Copyright 2008 Henrik Jonsson
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. *
  17. */
  18. using System;
  19. using System.Collections.Generic;
  20. using System.Linq;
  21. using System.Text;
  22. using System.Collections;
  23. using System.Linq.Expressions;
  24. namespace Dynamite
  25. {
  26. /// <summary>
  27. /// A Comparer for Nullable types whose underlying type is not comparable.
  28. /// </summary>
  29. /// <typeparam name="T">Underlying type</typeparam>
  30. /// <remarks>This comparer simply orders items that does not have a value before those that have a value.</remarks>
  31. public class JustNullNullableComparer<T> : IComparer<T?> where T : struct
  32. {
  33. private static JustNullNullableComparer<T> _default;
  34. private JustNullNullableComparer()
  35. {
  36. }
  37. public static JustNullNullableComparer<T> Default
  38. {
  39. get
  40. {
  41. if (_default == null)
  42. {
  43. _default = new JustNullNullableComparer<T>();
  44. }
  45. return _default;
  46. }
  47. }
  48. #region IComparer<T?> Members
  49. public int Compare(T? x, T? y)
  50. {
  51. if (x.HasValue)
  52. {
  53. if (y.HasValue) return 0;
  54. return 1;
  55. }
  56. else
  57. {
  58. if (y.HasValue) return -1;
  59. return 0;
  60. }
  61. }
  62. #endregion
  63. }
  64. /// <summary>
  65. /// A comparer for reference typed properties or fields which does not declare a IComparer interface.
  66. /// </summary>
  67. /// <typeparam name="T">Type to compare (must be a reference type)</typeparam>
  68. public sealed class LateBoundComparer<T> : IComparer<T> where T : class
  69. {
  70. private static LateBoundComparer<T> _default;
  71. private LateBoundComparer() { }
  72. public static LateBoundComparer<T> Default
  73. {
  74. get
  75. {
  76. if (_default == null)
  77. {
  78. _default = new LateBoundComparer<T>();
  79. }
  80. return _default;
  81. }
  82. }
  83. #region IComparer<T> Members
  84. public int Compare(T x, T y)
  85. {
  86. if (x != null)
  87. {
  88. if (y != null)
  89. {
  90. IComparable xc = x as IComparable;
  91. if (xc != null)
  92. {
  93. return xc.CompareTo(y);
  94. }
  95. else
  96. {
  97. return 0;
  98. }
  99. }
  100. else
  101. {
  102. return 1;
  103. }
  104. }
  105. else
  106. {
  107. if (y != null) return -1;
  108. return 0;
  109. }
  110. }
  111. #endregion
  112. }
  113. /// <summary>
  114. /// A "dummy" comparer that regards all values the same.
  115. /// </summary>
  116. /// <typeparam name="T">Type to compare</typeparam>
  117. public sealed class AlwaysEqualComparer<T> : IComparer<T>
  118. {
  119. private AlwaysEqualComparer() { }
  120. private static AlwaysEqualComparer<T> _default;
  121. public static AlwaysEqualComparer<T> Default
  122. {
  123. get
  124. {
  125. if (_default == null)
  126. {
  127. _default = new AlwaysEqualComparer<T>();
  128. }
  129. return _default;
  130. }
  131. }
  132. #region IComparer<T> Members
  133. public int Compare(T x, T y)
  134. {
  135. return 0;
  136. }
  137. #endregion
  138. }
  139. /// <summary>
  140. /// A comparer that compares Enum instances without the need for boxing.
  141. /// </summary>
  142. /// <typeparam name="T">Type to compare (must be an Enum type).</typeparam>
  143. public sealed class EnumComparer<T> : IComparer<T>
  144. {
  145. private EnumComparer() { }
  146. private static EnumComparer<T> _default;
  147. private static Comparison<T> _comparison;
  148. private static Comparison<T> CreateComparison()
  149. {
  150. if (typeof(T).IsEnum == false) throw new InvalidOperationException("Cannot create an EnumComparer for a non-enum type.");
  151. Type underlyingType = Enum.GetUnderlyingType(typeof(T));
  152. ParameterExpression x = Expression.Parameter(typeof(T), "x");
  153. ParameterExpression y = Expression.Parameter(typeof(T), "y");
  154. Expression body = Expression.Call(Expression.Convert(x,underlyingType), "CompareTo", Type.EmptyTypes,
  155. Expression.Convert(y,underlyingType));
  156. return Expression.Lambda<Comparison<T>>(body, x, y).Compile();
  157. }
  158. /// <summary>
  159. /// Gets a Comparison&lt;T&gt; that compares two instances.
  160. /// </summary>
  161. public static Comparison<T> Comparison
  162. {
  163. get
  164. {
  165. if (_comparison == null)
  166. {
  167. _comparison = CreateComparison();
  168. }
  169. return _comparison;
  170. }
  171. }
  172. /// <summary>
  173. /// Gets the singleton instance of this class.
  174. /// </summary>
  175. public static EnumComparer<T> Default
  176. {
  177. get
  178. {
  179. if (_default == null)
  180. {
  181. if (_comparison == null)
  182. {
  183. _comparison = CreateComparison();
  184. }
  185. _default = new EnumComparer<T>();
  186. }
  187. return _default;
  188. }
  189. }
  190. #region IComparer<T> Members
  191. /// <summary>
  192. /// Compares to instances.
  193. /// </summary>
  194. /// <param name="x">First instance to compare</param>
  195. /// <param name="y">Second comparand.</param>
  196. /// <returns></returns>
  197. public int Compare(T x, T y)
  198. {
  199. return _comparison(x, y);
  200. }
  201. #endregion
  202. }
  203. /// <summary>
  204. /// A comparer that compares Nullable types where the underlying type is an Enum type.
  205. /// </summary>
  206. /// <typeparam name="T">Underlying type to compare.</typeparam>
  207. public class NullableEnumComparer<T> : IComparer<T?> where T : struct
  208. {
  209. private NullableEnumComparer() { }
  210. private static NullableEnumComparer<T> _default;
  211. private static Comparison<T> _comparison;
  212. public static NullableEnumComparer<T> Default
  213. {
  214. get
  215. {
  216. if (_default == null)
  217. {
  218. _comparison = EnumComparer<T>.Comparison;
  219. _default = new NullableEnumComparer<T>();
  220. }
  221. return _default;
  222. }
  223. }
  224. #region IComparer<T> Members
  225. public int Compare(T? x, T? y)
  226. {
  227. if (x.HasValue)
  228. {
  229. if (y.HasValue)
  230. {
  231. return _comparison(x.GetValueOrDefault(), y.GetValueOrDefault());
  232. }
  233. else
  234. {
  235. return 1;
  236. }
  237. }
  238. else
  239. {
  240. if (y.HasValue)
  241. {
  242. return -1;
  243. }
  244. return 0;
  245. }
  246. }
  247. #endregion
  248. }
  249. /// <summary>
  250. /// An extended Comparer class that provides comparers that complements the normal System.Collections.Generic.Comparer&lt;T&gt; comparers.
  251. /// </summary>
  252. /// <typeparam name="T">Type to compare.</typeparam>
  253. /// <remarks>
  254. /// This class extends the System.Collections.Generic.Comparer&lt;T&gt; by returning optimized versions of comparers for enum
  255. /// types (including Nullable enum types). For reference types that does not implement any IComparer interface a
  256. /// LateBoundComparer&lt;T&gt; is returned that defines null values before non-null values and compares non-values based on
  257. /// IComparer implementation if they exists. For Nullable types whose underlying type is not comparable a
  258. /// JustNullNullableComparer&lt;T&gt; is returned that simple defines those values that do not have a value before values that
  259. /// does have a value. Finally, for other types of value types that does not implement a IComparable interface a comparer that
  260. /// regards all values equal are returned.
  261. /// </remarks>
  262. public class ExtendedComparer<T>
  263. {
  264. static IComparer<T> comparer;
  265. public static IComparer<T> Default
  266. {
  267. get {
  268. if (comparer == null)
  269. {
  270. comparer = CreateComparer();
  271. }
  272. return comparer;
  273. }
  274. }
  275. private static IComparer<T> CreateComparer()
  276. {
  277. Type propType = typeof(T);
  278. Type nullableUnderlyingType = Nullable.GetUnderlyingType(propType);
  279. if (nullableUnderlyingType != null)
  280. {
  281. propType = nullableUnderlyingType;
  282. }
  283. if (propType.IsEnum)
  284. {
  285. if (nullableUnderlyingType == null)
  286. {
  287. return EnumComparer<T>.Default;
  288. }
  289. else
  290. {
  291. return (IComparer<T>)typeof(NullableEnumComparer<>).MakeGenericType(propType).GetProperty("Default").GetGetMethod().Invoke(null, null);
  292. }
  293. }
  294. if (typeof(IComparer).IsAssignableFrom(propType) ||
  295. typeof(IComparable<>).MakeGenericType(propType).IsAssignableFrom(propType))
  296. {
  297. return Comparer<T>.Default;
  298. }
  299. else
  300. {
  301. if (propType.IsValueType)
  302. {
  303. if (nullableUnderlyingType != null)
  304. {
  305. return (IComparer<T>)typeof(JustNullNullableComparer<>).MakeGenericType(nullableUnderlyingType).GetProperty("Default").GetGetMethod().Invoke(null, null);
  306. }
  307. else
  308. {
  309. return AlwaysEqualComparer<T>.Default;
  310. }
  311. }
  312. else
  313. {
  314. return (IComparer<T>)typeof(LateBoundComparer<>).MakeGenericType(propType).GetProperty("Default").GetGetMethod().Invoke(null, null);
  315. }
  316. }
  317. }
  318. }
  319. }