Optional.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics.CodeAnalysis;
  4. #nullable enable
  5. namespace Avalonia.Data
  6. {
  7. /// <summary>
  8. /// An optional typed value.
  9. /// </summary>
  10. /// <typeparam name="T">The value type.</typeparam>
  11. /// <remarks>
  12. /// This struct is similar to <see cref="Nullable{T}"/> except it also accepts reference types:
  13. /// note that null is a valid value for reference types. It is also similar to
  14. /// <see cref="BindingValue{T}"/> but has only two states: "value present" and "value missing".
  15. ///
  16. /// To create a new optional value you can:
  17. ///
  18. /// - For a simple value, call the <see cref="Optional{T}"/> constructor or use an implicit
  19. /// conversion from <typeparamref name="T"/>
  20. /// - For an missing value, use <see cref="Empty"/> or simply `default`
  21. /// </remarks>
  22. public readonly struct Optional<T> : IEquatable<Optional<T>>
  23. {
  24. [AllowNull] private readonly T _value;
  25. /// <summary>
  26. /// Initializes a new instance of the <see cref="Optional{T}"/> struct with value.
  27. /// </summary>
  28. /// <param name="value">The value.</param>
  29. public Optional([AllowNull] T value)
  30. {
  31. _value = value;
  32. HasValue = true;
  33. }
  34. /// <summary>
  35. /// Gets a value indicating whether a value is present.
  36. /// </summary>
  37. public bool HasValue { get; }
  38. /// <summary>
  39. /// Gets the value.
  40. /// </summary>
  41. /// <exception cref="InvalidOperationException">
  42. /// <see cref="HasValue"/> is false.
  43. /// </exception>
  44. public T Value => HasValue ? _value : throw new InvalidOperationException("Optional has no value.");
  45. /// <inheritdoc/>
  46. public override bool Equals(object? obj) => obj is Optional<T> o && this == o;
  47. /// <inheritdoc/>
  48. public bool Equals(Optional<T> other) => this == other;
  49. /// <inheritdoc/>
  50. public override int GetHashCode() => HasValue ? _value?.GetHashCode() ?? 0 : 0;
  51. /// <summary>
  52. /// Casts the value (if any) to an <see cref="object"/>.
  53. /// </summary>
  54. /// <returns>The cast optional value.</returns>
  55. public Optional<object> ToObject() => HasValue ? new Optional<object>(_value) : default;
  56. /// <inheritdoc/>
  57. public override string ToString() => HasValue ? _value?.ToString() ?? "(null)" : "(empty)";
  58. /// <summary>
  59. /// Gets the value if present, otherwise the default value.
  60. /// </summary>
  61. /// <returns>The value.</returns>
  62. [return: MaybeNull]
  63. public T GetValueOrDefault() => HasValue ? _value : default;
  64. /// <summary>
  65. /// Gets the value if present, otherwise a default value.
  66. /// </summary>
  67. /// <param name="defaultValue">The default value.</param>
  68. /// <returns>The value.</returns>
  69. public T GetValueOrDefault(T defaultValue) => HasValue ? _value : defaultValue;
  70. /// <summary>
  71. /// Gets the value if present, otherwise the default value.
  72. /// </summary>
  73. /// <returns>
  74. /// The value if present and of the correct type, `default(TResult)` if the value is
  75. /// not present or of an incorrect type.
  76. /// </returns>
  77. [return: MaybeNull]
  78. public TResult GetValueOrDefault<TResult>()
  79. {
  80. return HasValue ?
  81. _value is TResult result ? result : default
  82. : default;
  83. }
  84. /// <summary>
  85. /// Gets the value if present, otherwise a default value.
  86. /// </summary>
  87. /// <param name="defaultValue">The default value.</param>
  88. /// <returns>
  89. /// The value if present and of the correct type, `default(TResult)` if the value is
  90. /// present but not of the correct type or null, or <paramref name="defaultValue"/> if the
  91. /// value is not present.
  92. /// </returns>
  93. [return: MaybeNull]
  94. public TResult GetValueOrDefault<TResult>([AllowNull] TResult defaultValue)
  95. {
  96. return HasValue ?
  97. _value is TResult result ? result : default
  98. : defaultValue;
  99. }
  100. /// <summary>
  101. /// Creates an <see cref="Optional{T}"/> from an instance of the underlying value type.
  102. /// </summary>
  103. /// <param name="value">The value.</param>
  104. public static implicit operator Optional<T>([AllowNull] T value) => new Optional<T>(value);
  105. /// <summary>
  106. /// Compares two <see cref="Optional{T}"/>s for inequality.
  107. /// </summary>
  108. /// <param name="x">The first value.</param>
  109. /// <param name="y">The second value.</param>
  110. /// <returns>True if the values are unequal; otherwise false.</returns>
  111. public static bool operator !=(Optional<T> x, Optional<T> y) => !(x == y);
  112. /// <summary>
  113. /// Compares two <see cref="Optional{T}"/>s for equality.
  114. /// </summary>
  115. /// <param name="x">The first value.</param>
  116. /// <param name="y">The second value.</param>
  117. /// <returns>True if the values are equal; otherwise false.</returns>
  118. public static bool operator ==(Optional<T> x, Optional<T> y)
  119. {
  120. if (!x.HasValue && !y.HasValue)
  121. {
  122. return true;
  123. }
  124. else if (x.HasValue && y.HasValue)
  125. {
  126. return EqualityComparer<T>.Default.Equals(x.Value, y.Value);
  127. }
  128. else
  129. {
  130. return false;
  131. }
  132. }
  133. /// <summary>
  134. /// Returns an <see cref="Optional{T}"/> without a value.
  135. /// </summary>
  136. public static Optional<T> Empty => default;
  137. }
  138. public static class OptionalExtensions
  139. {
  140. /// <summary>
  141. /// Casts the type of an <see cref="Optional{T}"/> using only the C# cast operator.
  142. /// </summary>
  143. /// <typeparam name="T">The target type.</typeparam>
  144. /// <param name="value">The binding value.</param>
  145. /// <returns>The cast value.</returns>
  146. public static Optional<T> Cast<T>(this Optional<object> value)
  147. {
  148. return value.HasValue ? new Optional<T>((T)value.Value) : Optional<T>.Empty;
  149. }
  150. }
  151. }