csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
170 lines
6.2 KiB
170 lines
6.2 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
|
#nullable enable
|
|
|
|
namespace Avalonia.Data
|
|
{
|
|
/// <summary>
|
|
/// An optional typed value.
|
|
/// </summary>
|
|
/// <typeparam name="T">The value type.</typeparam>
|
|
/// <remarks>
|
|
/// This struct is similar to <see cref="Nullable{T}"/> except it also accepts reference types:
|
|
/// note that null is a valid value for reference types. It is also similar to
|
|
/// <see cref="BindingValue{T}"/> but has only two states: "value present" and "value missing".
|
|
///
|
|
/// To create a new optional value you can:
|
|
///
|
|
/// - For a simple value, call the <see cref="Optional{T}"/> constructor or use an implicit
|
|
/// conversion from <typeparamref name="T"/>
|
|
/// - For an missing value, use <see cref="Empty"/> or simply `default`
|
|
/// </remarks>
|
|
public readonly struct Optional<T> : IEquatable<Optional<T>>
|
|
{
|
|
[AllowNull] private readonly T _value;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="Optional{T}"/> struct with value.
|
|
/// </summary>
|
|
/// <param name="value">The value.</param>
|
|
public Optional([AllowNull] T value)
|
|
{
|
|
_value = value;
|
|
HasValue = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether a value is present.
|
|
/// </summary>
|
|
public bool HasValue { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the value.
|
|
/// </summary>
|
|
/// <exception cref="InvalidOperationException">
|
|
/// <see cref="HasValue"/> is false.
|
|
/// </exception>
|
|
public T Value => HasValue ? _value : throw new InvalidOperationException("Optional has no value.");
|
|
|
|
/// <inheritdoc/>
|
|
public override bool Equals(object? obj) => obj is Optional<T> o && this == o;
|
|
|
|
/// <inheritdoc/>
|
|
public bool Equals(Optional<T> other) => this == other;
|
|
|
|
/// <inheritdoc/>
|
|
public override int GetHashCode() => HasValue ? _value?.GetHashCode() ?? 0 : 0;
|
|
|
|
/// <summary>
|
|
/// Casts the value (if any) to an <see cref="object"/>.
|
|
/// </summary>
|
|
/// <returns>The cast optional value.</returns>
|
|
public Optional<object> ToObject() => HasValue ? new Optional<object>(_value) : default;
|
|
|
|
/// <inheritdoc/>
|
|
public override string ToString() => HasValue ? _value?.ToString() ?? "(null)" : "(empty)";
|
|
|
|
/// <summary>
|
|
/// Gets the value if present, otherwise the default value.
|
|
/// </summary>
|
|
/// <returns>The value.</returns>
|
|
[return: MaybeNull]
|
|
public T GetValueOrDefault() => HasValue ? _value : default;
|
|
|
|
/// <summary>
|
|
/// Gets the value if present, otherwise a default value.
|
|
/// </summary>
|
|
/// <param name="defaultValue">The default value.</param>
|
|
/// <returns>The value.</returns>
|
|
public T GetValueOrDefault(T defaultValue) => HasValue ? _value : defaultValue;
|
|
|
|
/// <summary>
|
|
/// Gets the value if present, otherwise the default value.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// The value if present and of the correct type, `default(TResult)` if the value is
|
|
/// not present or of an incorrect type.
|
|
/// </returns>
|
|
[return: MaybeNull]
|
|
public TResult GetValueOrDefault<TResult>()
|
|
{
|
|
return HasValue ?
|
|
_value is TResult result ? result : default
|
|
: default;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the value if present, otherwise a default value.
|
|
/// </summary>
|
|
/// <param name="defaultValue">The default value.</param>
|
|
/// <returns>
|
|
/// The value if present and of the correct type, `default(TResult)` if the value is
|
|
/// present but not of the correct type or null, or <paramref name="defaultValue"/> if the
|
|
/// value is not present.
|
|
/// </returns>
|
|
[return: MaybeNull]
|
|
public TResult GetValueOrDefault<TResult>([AllowNull] TResult defaultValue)
|
|
{
|
|
return HasValue ?
|
|
_value is TResult result ? result : default
|
|
: defaultValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates an <see cref="Optional{T}"/> from an instance of the underlying value type.
|
|
/// </summary>
|
|
/// <param name="value">The value.</param>
|
|
public static implicit operator Optional<T>([AllowNull] T value) => new Optional<T>(value);
|
|
|
|
/// <summary>
|
|
/// Compares two <see cref="Optional{T}"/>s for inequality.
|
|
/// </summary>
|
|
/// <param name="x">The first value.</param>
|
|
/// <param name="y">The second value.</param>
|
|
/// <returns>True if the values are unequal; otherwise false.</returns>
|
|
public static bool operator !=(Optional<T> x, Optional<T> y) => !(x == y);
|
|
|
|
/// <summary>
|
|
/// Compares two <see cref="Optional{T}"/>s for equality.
|
|
/// </summary>
|
|
/// <param name="x">The first value.</param>
|
|
/// <param name="y">The second value.</param>
|
|
/// <returns>True if the values are equal; otherwise false.</returns>
|
|
public static bool operator ==(Optional<T> x, Optional<T> y)
|
|
{
|
|
if (!x.HasValue && !y.HasValue)
|
|
{
|
|
return true;
|
|
}
|
|
else if (x.HasValue && y.HasValue)
|
|
{
|
|
return EqualityComparer<T>.Default.Equals(x.Value, y.Value);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns an <see cref="Optional{T}"/> without a value.
|
|
/// </summary>
|
|
public static Optional<T> Empty => default;
|
|
}
|
|
|
|
public static class OptionalExtensions
|
|
{
|
|
/// <summary>
|
|
/// Casts the type of an <see cref="Optional{T}"/> using only the C# cast operator.
|
|
/// </summary>
|
|
/// <typeparam name="T">The target type.</typeparam>
|
|
/// <param name="value">The binding value.</param>
|
|
/// <returns>The cast value.</returns>
|
|
public static Optional<T> Cast<T>(this Optional<object> value)
|
|
{
|
|
return value.HasValue ? new Optional<T>((T)value.Value) : Optional<T>.Empty;
|
|
}
|
|
}
|
|
}
|
|
|