Optional<T>
public struct Optional<T> : IEquatable<Optional<T>>, IEquatable<T>, IStructuralEquatable, ISerializable
A container object which may or may not contain a value.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
namespace DotNext
{
[Serializable]
[StructLayout(LayoutKind.Auto)]
[System.Runtime.CompilerServices.NullableContext(1)]
[System.Runtime.CompilerServices.Nullable(0)]
public readonly struct Optional<[System.Runtime.CompilerServices.Nullable(2)] T> : IEquatable<Optional<T>>, IEquatable<T>, IStructuralEquatable, ISerializable
{
private const string KindSerData = "Kind";
private const string ValueSerData = "Value";
private const byte UndefinedValue = 0;
private const byte NullValue = 1;
private const byte NotEmptyValue = 3;
private static readonly bool IsOptional;
[AllowNull]
private readonly T value;
private readonly byte kind;
[System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
[Obsolete("Use None static property instead")]
public static Optional<T> Empty {
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
get {
return None;
}
}
[System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
public static Optional<T> None {
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
get {
return default(Optional<T>);
}
}
public bool HasValue => kind == 3;
public bool IsUndefined => kind == 0;
public bool IsNull => kind == 1;
public T Value {
get {
string message;
switch (kind) {
default:
return value;
case 0:
message = ExceptionMessages.OptionalNoValue;
break;
case 1:
message = ExceptionMessages.OptionalNullValue;
break;
}
throw new InvalidOperationException(message);
}
}
static Optional()
{
Type typeFromHandle = typeof(T);
IsOptional = (typeFromHandle.IsConstructedGenericType && typeFromHandle.GetGenericTypeDefinition() == typeof(Optional<>));
}
public Optional([AllowNull] T value)
{
this.value = value;
if (value == null)
kind = 1;
else if (IsOptional) {
kind = GetKindUnsafe(ref value);
} else {
kind = 3;
}
}
private static byte GetKindUnsafe([DisallowNull] ref T optionalValue)
{
if (optionalValue.Equals(null))
return 1;
if (optionalValue.Equals(Missing.Value))
return 0;
return 3;
}
private Optional(SerializationInfo info, StreamingContext context)
{
value = (T)info.GetValue("Value", typeof(T));
kind = info.GetByte("Kind");
}
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
public Optional<object> Box()
{
if (!HasValue)
return default(Optional<object>);
return new Optional<object>(value);
}
public bool TryGet([NotNullWhen(true)] out T value)
{
value = this.value;
return HasValue;
}
public bool TryGet([NotNullWhen(true)] out T value, out bool isNull)
{
value = this.value;
switch (kind) {
default:
isNull = false;
return false;
case 1:
isNull = true;
return false;
case 3:
isNull = false;
return true;
}
}
[return: NotNullIfNotNull("defaultValue")]
public T Or(T defaultValue)
{
if (!HasValue)
return defaultValue;
return value;
}
[return: NotNull]
public T OrThrow<[System.Runtime.CompilerServices.Nullable(0)] TException>() where TException : Exception, new
{
if (!this.HasValue)
throw new TException();
return this.value;
}
[return: NotNull]
public T OrThrow<[System.Runtime.CompilerServices.Nullable(0)] TException>([In] [IsReadOnly] [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ref ValueFunc<TException> exceptionFactory) where TException : Exception
{
if (!this.HasValue)
throw exceptionFactory.Invoke();
return this.value;
}
[return: NotNull]
public T OrThrow<[System.Runtime.CompilerServices.Nullable(0)] TException>(Func<TException> exceptionFactory) where TException : Exception
{
ValueFunc<TException> exceptionFactory2 = new ValueFunc<TException>(exceptionFactory, true);
return OrThrow(ref exceptionFactory2);
}
public T OrInvoke([In] [IsReadOnly] [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ref ValueFunc<T> defaultFunc)
{
if (!HasValue)
return defaultFunc.Invoke();
return value;
}
public T OrInvoke(Func<T> defaultFunc)
{
ValueFunc<T> defaultFunc2 = new ValueFunc<T>(defaultFunc, true);
return OrInvoke(ref defaultFunc2);
}
public T OrDefault()
{
return value;
}
[System.Runtime.CompilerServices.NullableContext(2)]
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
public Optional<TResult> Convert<TResult>([In] [IsReadOnly] [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1,
1
})] ref ValueFunc<T, TResult> mapper)
{
if (!this.HasValue)
return Optional<TResult>.None;
return mapper.Invoke(this.value);
}
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
public Optional<TResult> Convert<[System.Runtime.CompilerServices.Nullable(2)] TResult>(Converter<T, TResult> mapper)
{
ValueFunc<T, TResult> mapper2 = Converter.AsValueFunc<T, TResult>(mapper, true);
return Convert(ref mapper2);
}
[System.Runtime.CompilerServices.NullableContext(2)]
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
public Optional<TResult> Convert<TResult>([In] [IsReadOnly] [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1,
0,
1
})] ref ValueFunc<T, Optional<TResult>> mapper)
{
if (!this.HasValue)
return Optional<TResult>.None;
return mapper.Invoke(this.value);
}
[System.Runtime.CompilerServices.NullableContext(2)]
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
public Optional<TResult> Convert<TResult>([System.Runtime.CompilerServices.Nullable(new byte[] {
1,
1,
0,
1
})] Converter<T, Optional<TResult>> mapper)
{
ValueFunc<T, Optional<TResult>> mapper2 = Converter.AsValueFunc<T, Optional<TResult>>(mapper, true);
return Convert(ref mapper2);
}
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
public Optional<T> If([In] [IsReadOnly] [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ref ValueFunc<T, bool> condition)
{
if (!HasValue || !condition.Invoke(value))
return None;
return this;
}
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
public Optional<T> If(Predicate<T> condition)
{
ValueFunc<T, bool> condition2 = Predicate.AsValueFunc<T>(condition, true);
return If(ref condition2);
}
public override string ToString()
{
switch (kind) {
case 0:
return "<Undefined>";
case 1:
return "<Null>";
default:
return value.ToString();
}
}
public override int GetHashCode()
{
if (!HasValue)
return 0;
return EqualityComparer<T>.Default.GetHashCode(value);
}
public bool Equals(T other)
{
if (HasValue)
return EqualityComparer<T>.Default.Equals(value, other);
return false;
}
private bool Equals([In] [IsReadOnly] [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ref Optional<T> other)
{
int num = kind + other.kind;
if ((uint)(num - 3) <= 1)
return false;
if (num != 6)
return true;
return EqualityComparer<T>.Default.Equals(value, other.value);
}
public bool Equals([System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] Optional<T> other)
{
return Equals(ref other);
}
[System.Runtime.CompilerServices.NullableContext(2)]
public override bool Equals(object other)
{
if (other == null)
return kind == 1;
if (other is Optional<T>) {
Optional<T> other2 = (Optional<T>)other;
return Equals(ref other2);
}
if (other is T) {
T other3 = (T)other;
return Equals(other3);
}
if (other == Missing.Value)
return kind == 0;
return false;
}
public bool Equals([System.Runtime.CompilerServices.Nullable(2)] object other, IEqualityComparer comparer)
{
if (other is T && HasValue)
return comparer.Equals(value, other);
return false;
}
public int GetHashCode(IEqualityComparer comparer)
{
if (!HasValue)
return 0;
return comparer.GetHashCode(value);
}
public static implicit operator Optional<T>([AllowNull] T value)
{
return new Optional<T>(value);
}
public static explicit operator T([In] [IsReadOnly] [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ref Optional<T> optional)
{
return optional.Value;
}
public static bool operator ==([In] [IsReadOnly] [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ref Optional<T> first, [In] [IsReadOnly] [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ref Optional<T> second)
{
return first.Equals(ref second);
}
public static bool operator !=([In] [IsReadOnly] [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ref Optional<T> first, [In] [IsReadOnly] [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ref Optional<T> second)
{
return !first.Equals(ref second);
}
public static Optional<T>operator |([In] [IsReadOnly] [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ref Optional<T> first, [In] [IsReadOnly] [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ref Optional<T> second)
{
if (!first.HasValue)
return second;
return first;
}
public static Optional<T>operator ^([In] [IsReadOnly] [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ref Optional<T> first, [In] [IsReadOnly] [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ref Optional<T> second)
{
int num = first.kind - second.kind;
if ((uint)(num - -3) <= 2)
return second;
if ((uint)(num - 1) > 2)
return default(Optional<T>);
return first;
}
public static bool operator true([In] [IsReadOnly] [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ref Optional<T> optional)
{
return optional.HasValue;
}
public static bool operator false([In] [IsReadOnly] [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ref Optional<T> optional)
{
return optional.kind < 3;
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Value", value, typeof(T));
info.AddValue("Kind", kind);
}
}
}