using System; using System.Collections; using System.Collections.Generic; using System.Linq; namespace mRemoteNG.Tools { /// /// Represents a type that may or may not have been assigned a value. /// A strongly typed collection that contains either 0 or 1 values. /// /// The underlying type that may or may not have a value public class Optional : IEnumerable, IComparable> { private readonly T[] _optional; /// /// Create a new empty instance of Optional /// public Optional() { _optional = new T[0]; } /// /// Create a new instance of Optional from the given value. /// If the value is null, the Optional will be empty /// public Optional(T value) { _optional = value != null ? new[] {value} : new T[0]; } public override string ToString() { return _optional.Any() ? _optional.First().ToString() : ""; } public static implicit operator Optional(T value) { return new Optional(value); } public static Optional FromNullable(TOut? value) where TOut : struct { return value.HasValue ? new Optional(value.Value) : new Optional(); } /// /// Returns an empty /// public static Optional Empty => new Optional(); #region IEnumerable IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public IEnumerator GetEnumerator() { return ((IEnumerable)_optional).GetEnumerator(); } #endregion #region IComparable /// /// Compares this to another instance /// of the same type. For purposes of comparison, empty Optional /// objects are treated like Null and will be valued lower than /// an Optional that contains a value. If both Optionals contain /// values, the values are compared directly. /// /// public int CompareTo(Optional other) { var otherHasAnything = other.Any(); var thisHasAnything = _optional.Length > 0; // both are empty, equivalent value if (!thisHasAnything && !otherHasAnything) return 0; // we are empty, they are greater value if (!thisHasAnything) return -1; // they are empty, we are greater value if (!otherHasAnything) return 1; // neither are empty, compare wrapped objects directly if (_optional[0] is IComparable) return ((IComparable)_optional[0]).CompareTo(other.First()); throw new ArgumentException(string.Format( "Cannot compare objects. Optional type {0} is not comparable to itself", typeof(T).FullName)); } #endregion #region Override Equals and GetHashCode public override bool Equals(object obj) { if (ReferenceEquals(this, obj)) return true; var objAsOptional = obj as Optional; if (objAsOptional != null) return Equals(objAsOptional); if (obj is T) Equals((T)obj); return false; } public bool Equals(Optional other) { var otherObj = other.FirstOrDefault(); var thisObj = _optional.FirstOrDefault(); if (thisObj == null && otherObj == null) return true; if (thisObj == null) return false; return thisObj.Equals(otherObj); } public override int GetHashCode() { return _optional != null ? _optional.GetHashCode() : 0; } #endregion #region Operators public static bool operator ==(Optional left, Optional right) { return Equals(left, right); } public static bool operator !=(Optional left, Optional right) { return !Equals(left, right); } #endregion } }