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
}
}