Created
May 5, 2017 14:18
-
-
Save gusmanb/fc6662d64195d4230164a7700fdaa135 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <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<T> 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<T> 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