Option.cs 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. namespace Asset_Cleaner {
  5. readonly struct Option<T> : IEquatable<Option<T>>, IComparable<Option<T>> {
  6. // ReSharper disable once StaticMemberInGenericType
  7. static readonly bool IsValueType;
  8. public bool HasValue { get; }
  9. T Value { get; }
  10. public static implicit operator Option<T>(T arg) {
  11. if (!IsValueType) return ReferenceEquals(arg, null) ? new Option<T>() : new Option<T>(arg, true);
  12. #if M_WARN
  13. if (arg.Equals(default(T)))
  14. Warn.Warning($"{arg} has default value");
  15. #endif
  16. return new Option<T>(arg, true);
  17. }
  18. static Option() {
  19. IsValueType = typeof(T).IsValueType;
  20. }
  21. public void GetOrFail(out T value) {
  22. if (!TryGet(out value))
  23. Fail($"Option<{typeof(T).Name}> has no value");
  24. }
  25. public T GetOrFail() {
  26. if (!TryGet(out var value))
  27. Fail($"Option<{typeof(T).Name}> has no value");
  28. return value;
  29. }
  30. [Conditional("DEBUG1")]
  31. static void Fail(string format = null) {
  32. throw new Exception(format);
  33. }
  34. public bool TryGet(out T value) {
  35. if (!HasValue) {
  36. value = default(T);
  37. return false;
  38. }
  39. value = Value;
  40. return true;
  41. }
  42. internal Option(T value, bool hasValue) {
  43. Value = value;
  44. HasValue = hasValue;
  45. }
  46. public T ValueOr(T alternative) {
  47. return HasValue ? Value : alternative;
  48. }
  49. // for debug purposes
  50. public override string ToString() {
  51. if (!HasValue) return "None";
  52. return Value == null ? "Some(null)" : $"Some({Value})";
  53. }
  54. #region eq comparers boilerplate
  55. public bool Equals(Option<T> other) {
  56. if (!HasValue && !other.HasValue)
  57. return true;
  58. if (HasValue && other.HasValue)
  59. return EqualityComparer<T>.Default.Equals(Value, other.Value);
  60. return false;
  61. }
  62. public override bool Equals(object obj) {
  63. return obj is Option<T> && Equals((Option<T>) obj);
  64. }
  65. public static bool operator ==(Option<T> left, Option<T> right) {
  66. return left.Equals(right);
  67. }
  68. public static bool operator !=(Option<T> left, Option<T> right) {
  69. return !left.Equals(right);
  70. }
  71. public override int GetHashCode() {
  72. if (!HasValue) return 0;
  73. return IsValueType || Value != null ? Value.GetHashCode() : 1;
  74. }
  75. public int CompareTo(Option<T> other) {
  76. if (HasValue && !other.HasValue) return 1;
  77. if (!HasValue && other.HasValue) return -1;
  78. return Comparer<T>.Default.Compare(Value, other.Value);
  79. }
  80. public static bool operator <(Option<T> left, Option<T> right) {
  81. return left.CompareTo(right) < 0;
  82. }
  83. public static bool operator <=(Option<T> left, Option<T> right) {
  84. return left.CompareTo(right) <= 0;
  85. }
  86. public static bool operator >(Option<T> left, Option<T> right) {
  87. return left.CompareTo(right) > 0;
  88. }
  89. public static bool operator >=(Option<T> left, Option<T> right) {
  90. return left.CompareTo(right) >= 0;
  91. }
  92. #endregion
  93. }
  94. }