Skip to content

Instantly share code, notes, and snippets.

@gusmanb
Created May 5, 2017 14:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gusmanb/fc6662d64195d4230164a7700fdaa135 to your computer and use it in GitHub Desktop.
Save gusmanb/fc6662d64195d4230164a7700fdaa135 to your computer and use it in GitHub Desktop.
/// <summary>
/// Optional class, handy functions for Optional parameter handling
/// </summary>
public static class Optional
{
/// <summary>
/// Compares two instances of Optional.
/// </summary>
/// <typeparam name="T">Type of underlying value</typeparam>
/// <param name="n1">First Optional to compare</param>
/// <param name="n2">Second Optional to compare</param>
/// <returns></returns>
public static int Compare<T>(Optional<T> n1, Optional<T> n2)
{
if (n1.isSet)
{
if (n2.isSet) return Comparer<T>.Default.Compare(n1.value, n2.value);
return 1;
}
if (n2.IsSet) return -1;
return 0;
}
/// <summary>
/// Compares the specified Optional instances.
/// </summary>
/// <typeparam name="T">Type of underlying value</typeparam>
/// <param name="n1">First Optional to compare</param>
/// <param name="n2">Second Optional to compare</param>
/// <returns>True if they're equal, false in other case</returns>
public static bool Equals<T>(Optional<T> n1, Optional<T> n2)
{
if (n1.isSet)
{
if (n2.isSet) return EqualityComparer<T>.Default.Equals(n1.value, n2.value);
return false;
}
if (n2.isSet) return false;
return true;
}
/// <summary>
/// Gets the type of the underlying value.
/// </summary>
/// <param name="optionalType">Type of the optional.</param>
/// <returns>The underlying type</returns>
/// <exception cref="System.ArgumentNullException">optionalType</exception>
public static Type GetUnderlyingType(Type optionalType)
{
if ((object)optionalType == null)
{
throw new ArgumentNullException("optionalType");
}
Type result = null;
var info = optionalType.GetTypeInfo();
if (info.IsGenericType && !info.IsGenericTypeDefinition)
{
Type genericType = optionalType.GetGenericTypeDefinition();
if (Object.ReferenceEquals(genericType, typeof(Optional<>)))
{
result = info.GetGenericArguments()[0];
}
}
return result;
}
}
public interface IOptional
{
bool IsSet { get; }
object ObjectValue { get; }
}
/// <summary>
/// Struc to hold optional parameters where an "undefined" state would be desirable, meant to be used with DTOs.
/// Optional parameters which aren't set will not be serialized.
/// Optional values which never had it's value set will have the "IsSet" property as false, in
/// this way it can be distinguished between "null" values and "undefined" values.
/// </summary>
/// <typeparam name="T">Type of underlying value</typeparam>
public struct Optional<T> : IOptional
{
/// <summary>
/// Gets an instance of Optional&lt;T&gt; in it's "undefined" state
/// </summary>
/// <value>
/// The instance.
/// </value>
public static Optional<T> Undefined { get { return default(Optional<T>); } }
/// <summary>
/// Gets an instance of Optional&lt;T&gt; in it's "default" state (value will have it's default value set, IsSet will be true)
/// </summary>
/// <value>
/// The instance.
/// </value>
public static Optional<T> Default
{
get
{
var val = default(Optional<T>);
val.isSet = true;
return val;
}
}
internal T value;
internal bool isSet;
/// <summary>
/// Initializes a new instance of the <see cref="Optional{T}"/> struct.
/// </summary>
/// <param name="Value">The value to set.</param>
/// <remarks>Using this constructor IsSet will be true</remarks>
public Optional(T Value)
{
isSet = true;
value = Value;
}
/// <summary>
/// Gets a value indicating whether this instance is set.
/// </summary>
/// <value>
/// <c>true</c> if this instance is set; otherwise, <c>false</c>.
/// </value>
public bool IsSet { get { return isSet; } }
/// <summary>
/// Gets or sets the underlying value.
/// </summary>
/// <value>
/// The value.
/// </value>
public T Value
{
get { return value; }
set { isSet = true; this.value = value; }
}
public object ObjectValue { get { return value; } }
/// <summary>
/// Performs an implicit conversion from T to <see cref="Optional{T}"/>.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>
/// The result of the conversion.
/// </returns>
public static implicit operator Optional<T>(T value)
{
return new Optional<T>(value);
}
/// <summary>
/// Performs an implicit conversion from <see cref="Optional{T}"/> to T, it avoids exceptions, if value is not set it returns the default T.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>
/// The result of the conversion.
/// </returns>
public static implicit operator T(Optional<T> value)
{
return value.value;
}
/// <summary>
/// Gets the underlying value or undefinedValue if IsSet is false.
/// </summary>
/// <param name="undefinedValue">The value to return if IsSet is flse.</param>
/// <returns>Value if IsSet is true, else undefinedValue</returns>
public T GetValueOrUndefined(T undefinedValue)
{
if (!isSet)
return undefinedValue;
return value;
}
/// <summary>
/// Implements the operator ==.
/// </summary>
/// <param name="t1">The first Optional instance.</param>
/// <param name="t2">The second Optional instance.</param>
/// <returns>
/// The result of the operator.
/// </returns>
public static bool operator ==(Optional<T> t1, Optional<T> t2)
{
// undefined equals undefined
if (!t1.isSet && !t2.isSet) return true;
// undefined != everything else
if (t1.isSet ^ t2.isSet) return false;
// null equals null
if (t1 == null && t2 == null) return true;
// null != everything else
if ((t1 == null) ^ (t2 == null)) return false;
// if both are values, compare them
return t1.value.Equals(t2.value);
}
/// <summary>
/// Implements the operator !=.
/// </summary>
/// <param name="t1">The first Optional instance.</param>
/// <param name="t2">The second Optional instance.</param>
/// <returns>
/// The result of the operator.
/// </returns>
public static bool operator !=(Optional<T> t1, Optional<T> t2)
{
return !(t1 == t2);
}
/// <summary>
/// Implements the operator == for Optional and the type of it's underlying value.
/// </summary>
/// <param name="t1">The Optional instance to compare</param>
/// <param name="t2">The T instance to compare</param>
/// <returns>
/// The result of the operator.
/// </returns>
public static bool operator ==(Optional<T> t1, T t2)
{
// undefined equals null (for sanity)
if (!t1.isSet && t2 == null) return true;
// null equals null
if (t1 == null && t2 == null) return true;
// null != everything else
if ((t1 == null) ^ (t2 == null)) return false;
// if both are values, compare them
return t1.value.Equals(t2);
}
/// <summary>
/// Implements the operator != for Optional and the type of it's underlying value.
/// </summary>
/// <param name="t1">The Optional instance to compare</param>
/// <param name="t2">The T instance to compare</param>
/// <returns>
/// The result of the operator.
/// </returns>
public static bool operator !=(Optional<T> t1, T t2)
{
return !(t1 == t2);
}
/// <summary>
/// Returns a hash code for this instance.
/// </summary>
/// <returns>
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
/// </returns>
public override int GetHashCode()
{
if (!isSet) return 0;
return value.GetHashCode();
}
/// <summary>
/// Determines whether the specified <see cref="System.Object" />, is equal to this instance.
/// </summary>
/// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
public override bool Equals(object obj)
{
return base.Equals(obj);
}
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
public override string ToString()
{
return isSet ?
(value != null ? value.ToString() : "null") :
"undefined";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment