ValueRange.cs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. namespace MeloongCore;
  2. /// <summary>
  3. /// 表示一个由可选下界和可选上界组成的范围。
  4. /// </summary>
  5. public sealed class ValueRange<T>(
  6. T? lower, T? upper,
  7. bool isLowerInclusive, bool isUpperInclusive,
  8. bool hasLower, bool hasUpper
  9. ) : IEquatable<ValueRange<T>?> where T : IComparable<T> {
  10. #region 基础字段
  11. public T? Lower { get; private set; } = hasLower && lower is null ? throw new ArgumentNullException(nameof(lower)) : lower;
  12. public T? Upper { get; private set; } = hasUpper && upper is null ? throw new ArgumentNullException(nameof(upper)) : upper;
  13. public bool HasLower { get; private set; } = hasLower;
  14. public bool HasUpper { get; private set; } = hasUpper;
  15. public void ClearLower() { Lower = default; HasLower = false; }
  16. public void ClearUpper() { Upper = default; HasUpper = false; }
  17. public void SetLower(T lower, bool isInclusive) {
  18. Lower = lower;
  19. HasLower = true;
  20. IsLowerInclusive = isInclusive;
  21. }
  22. public void SetUpper(T upper, bool isInclusive) {
  23. Upper = upper;
  24. HasUpper = true;
  25. IsUpperInclusive = isInclusive;
  26. }
  27. /// <summary> 下界值是否为闭区间。 </summary>
  28. public bool IsLowerInclusive = isLowerInclusive;
  29. /// <summary> 上界值是否为闭区间。 </summary>
  30. public bool IsUpperInclusive = isUpperInclusive;
  31. #endregion
  32. #region ClosedOpen、AtLeast 等工厂方法
  33. /// <summary> 创建等同于 <c>[lower, upper]</c> 的闭区间。 </summary>
  34. public static ValueRange<T> Closed(T lower, T upper) => new(lower, upper, true, true, true, true);
  35. /// <summary> 创建形如 <c>[lower, upper)</c> 的左闭右开区间。 </summary>
  36. public static ValueRange<T> ClosedOpen(T lower, T upper) => new(lower, upper, true, false, true, true);
  37. /// <summary> 创建形如 <c>(lower, upper]</c> 的左开右闭区间。 </summary>
  38. public static ValueRange<T> OpenClosed(T lower, T upper) => new(lower, upper, false, true, true, true);
  39. /// <summary> 创建形如 <c>(lower, upper)</c> 的开区间。 </summary>
  40. public static ValueRange<T> Open(T lower, T upper) => new(lower, upper, false, false, true, true);
  41. /// <summary> 创建形如 <c>[value, value]</c> 的单值闭区间。 </summary>
  42. public static ValueRange<T> Exactly(T value) => new(value, value, true, true, true, true);
  43. /// <summary> 创建形如 <c>[lower, +∞)</c> 的范围。 </summary>
  44. public static ValueRange<T> AtLeast(T lower) => new(lower, default, true, true, true, false);
  45. /// <summary> 创建形如 <c>(lower, +∞)</c> 的范围。 </summary>
  46. public static ValueRange<T> GreaterThan(T lower) => new(lower, default, false, true, true, false);
  47. /// <summary> 创建形如 <c>(-∞, upper]</c> 的范围。 </summary>
  48. public static ValueRange<T> AtMost(T upper) => new(default, upper, true, true, false, true);
  49. /// <summary> 创建形如 <c>(-∞, upper)</c> 的范围。 </summary>
  50. public static ValueRange<T> LessThan(T upper) => new(default, upper, true, false, false, true);
  51. /// <summary> 创建形如 <c>(-∞, +∞)</c> 的无限范围。 </summary>
  52. public static ValueRange<T> All() => new(default, default, true, true, false, false);
  53. #endregion
  54. #region 字符串双向转换
  55. /// <summary>
  56. /// 从字符串解析 <see cref="ValueRange{T}"/> 实例。
  57. /// </summary>
  58. public static ValueRange<T> FromString(string str, Func<string, T>? parseFunc = null) {
  59. if (string.IsNullOrEmpty(str)) throw new ArgumentException("不能为空");
  60. var isLowerInclusive = str[0] == '[';
  61. var isUpperInclusive = str[^1] == ']';
  62. if (!isLowerInclusive && str[0] != '(') throw new ArgumentException("开头必须是 [ 或 (");
  63. if (!isUpperInclusive && str[^1] != ')') throw new ArgumentException("结尾必须是 ] 或 )");
  64. var commaIndex = str.IndexOfAny([',', ',']);
  65. if (commaIndex == -1) throw new ArgumentException("需要用逗号分隔两个值");
  66. var lowerStr = str[1..commaIndex].Trim();
  67. var upperStr = str[(commaIndex + 1)..^1].Trim();
  68. T? lower = default;
  69. T? upper = default;
  70. bool hasLower = false;
  71. bool hasUpper = false;
  72. if (lowerStr is not ("" or "-∞")) {
  73. lower = parseFunc != null ? parseFunc(lowerStr) : (T) Convert.ChangeType(lowerStr, typeof(T));
  74. hasLower = true;
  75. }
  76. if (upperStr is not ("" or "+∞" or "∞")) {
  77. upper = parseFunc != null ? parseFunc(upperStr) : (T) Convert.ChangeType(upperStr, typeof(T));
  78. hasUpper = true;
  79. }
  80. return new(lower, upper, isLowerInclusive, isUpperInclusive, hasLower, hasUpper);
  81. }
  82. public override string ToString()
  83. => $"{(HasLower && IsLowerInclusive ? "[" : "(")}{(HasLower ? Lower!.ToString() : "-∞")}, {(HasUpper ? Upper!.ToString() : "+∞")}{(HasUpper && IsUpperInclusive ? "]" : ")")}";
  84. #endregion
  85. /// <summary>
  86. /// 判断指定值是否位于当前范围内。
  87. /// </summary>
  88. public bool Contains(T value) {
  89. if (HasLower) {
  90. var compareLower = value.CompareTo(Lower!);
  91. if (compareLower < 0) return false;
  92. if (compareLower == 0 && !IsLowerInclusive) return false;
  93. }
  94. if (HasUpper) {
  95. var compareUpper = value.CompareTo(Upper!);
  96. if (compareUpper > 0) return false;
  97. if (compareUpper == 0 && !IsUpperInclusive) return false;
  98. }
  99. return true;
  100. }
  101. /// <summary>
  102. /// 该范围是否不包含任何值。
  103. /// </summary>
  104. public bool IsEmpty() {
  105. if (!HasLower || !HasUpper) return false;
  106. int compared = Lower!.CompareTo(Upper!);
  107. return compared > 0 || (compared == 0 && (!IsLowerInclusive || !IsUpperInclusive));
  108. }
  109. /// <summary>
  110. /// 取当前范围与另一个范围的交集。
  111. /// </summary>
  112. /// <returns> 若两个范围存在交集,则返回交集范围;否则返回 <see langword="null" />。 </returns>
  113. public ValueRange<T> Intersect(ValueRange<T> other) {
  114. T? lower = default;
  115. T? upper = default;
  116. var isLowerInclusive = true;
  117. var isUpperInclusive = true;
  118. if (HasLower && other.HasLower) {
  119. switch (Lower!.CompareTo(other.Lower!)) {
  120. case > 0:
  121. lower = Lower;
  122. isLowerInclusive = IsLowerInclusive;
  123. break;
  124. case < 0:
  125. lower = other.Lower;
  126. isLowerInclusive = other.IsLowerInclusive;
  127. break;
  128. default:
  129. lower = Lower;
  130. isLowerInclusive = IsLowerInclusive && other.IsLowerInclusive;
  131. break;
  132. }
  133. } else if (HasLower) {
  134. lower = Lower;
  135. isLowerInclusive = IsLowerInclusive;
  136. } else if (other.HasLower) {
  137. lower = other.Lower;
  138. isLowerInclusive = other.IsLowerInclusive;
  139. }
  140. if (HasUpper && other.HasUpper) {
  141. switch (Upper!.CompareTo(other.Upper!)) {
  142. case < 0:
  143. upper = Upper;
  144. isUpperInclusive = IsUpperInclusive;
  145. break;
  146. case > 0:
  147. upper = other.Upper;
  148. isUpperInclusive = other.IsUpperInclusive;
  149. break;
  150. default:
  151. upper = Upper;
  152. isUpperInclusive = IsUpperInclusive && other.IsUpperInclusive;
  153. break;
  154. }
  155. } else if (HasUpper) {
  156. upper = Upper;
  157. isUpperInclusive = IsUpperInclusive;
  158. } else if (other.HasUpper) {
  159. upper = other.Upper;
  160. isUpperInclusive = other.IsUpperInclusive;
  161. }
  162. var anyHasLower = HasLower || other.HasLower;
  163. var anyHasUpper = HasUpper || other.HasUpper;
  164. return new(lower, upper, isLowerInclusive, isUpperInclusive, anyHasLower, anyHasUpper);
  165. }
  166. #region 相等判断(自动生成)
  167. public override bool Equals(object? obj) => Equals(obj as ValueRange<T>);
  168. public bool Equals(ValueRange<T>? other) {
  169. return other is not null&&
  170. EqualityComparer<T?>.Default.Equals(Lower, other.Lower)&&
  171. EqualityComparer<T?>.Default.Equals(Upper, other.Upper)&&
  172. HasLower==other.HasLower&&
  173. HasUpper==other.HasUpper&&
  174. IsLowerInclusive==other.IsLowerInclusive&&
  175. IsUpperInclusive==other.IsUpperInclusive;
  176. }
  177. public override int GetHashCode() {
  178. int hashCode = 169504101;
  179. hashCode=hashCode*-1521134295+EqualityComparer<T?>.Default.GetHashCode(lower);
  180. hashCode=hashCode*-1521134295+EqualityComparer<T?>.Default.GetHashCode(upper);
  181. hashCode=hashCode*-1521134295+hasLower.GetHashCode();
  182. hashCode=hashCode*-1521134295+hasUpper.GetHashCode();
  183. hashCode=hashCode*-1521134295+isLowerInclusive.GetHashCode();
  184. hashCode=hashCode*-1521134295+isUpperInclusive.GetHashCode();
  185. return hashCode;
  186. }
  187. #pragma warning disable CS8604 // 引用类型参数可能为 null。
  188. public static bool operator ==(ValueRange<T>? left, ValueRange<T>? right) => EqualityComparer<ValueRange<T>>.Default.Equals(left, right);
  189. #pragma warning restore CS8604 // 引用类型参数可能为 null。
  190. public static bool operator !=(ValueRange<T>? left, ValueRange<T>? right) => !(left==right);
  191. #endregion
  192. }