JD2022-TU1/main/tools/framework/JD.Kernel/Structs/Flags.cs

316 lines
No EOL
8.1 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
namespace JD.Kernel.Structs
{
/// <summary>
/// Encapsulate a flag enum and contains helper functions to manipulate it.
/// </summary>
public struct Flags<T> : IEquatable<T>
where T : IComparable, IFormattable
{
#region Variables
private T m_value;
#endregion
#region Constructor
static Flags()
{
Type type = typeof (T);
if (!type.IsEnum)
{
throw new InvalidCastException($"The flags type cannot be used with the non enum type {typeof(T).Name}");
}
if (!type.IsDefined(typeof(FlagsAttribute), true))
{
throw new InvalidCastException($"The enum type {typeof(T).Name} should be decorated with the flags attribute. Consider using a BitArray");
}
if (Enum.GetUnderlyingType(type) != typeof(int))
{
throw new NotSupportedException("Flags class only support int enums.");
}
}
public Flags(T value)
{
m_value = value;
}
#endregion
#region Properties
/// <summary>
/// Returns the actual wrapped value.
/// </summary>
public T Value
{
get { return m_value; }
}
private int IntValue
{
get { return ToInt(m_value); }
}
/// <summary>
/// Gets/Sets the value of the given flagKey.
/// </summary>
/// <param name="flagKey">Flag to access.</param>
/// <returns>The value of the flag.</returns>
public bool this[T flagKey]
{
get { return Contains(flagKey); }
set
{
if (value)
{
Add(flagKey);
}
else
{
Remove(flagKey);
}
}
}
#endregion
#region Functions
private static int ToInt(T value)
{
return (int)(object)value;
}
/// <summary>
/// Removes the given flag from the enum value.
/// </summary>
/// <param name="flag"></param>
/// <returns>True if the flag has been removed.</returns>
public bool Remove(T flag)
{
int value = IntValue;
int flagToExtract = ToInt(flag);
int newValue = value & ~flagToExtract;
m_value = (T)(object)newValue;
return value != newValue;
}
/// <summary>
/// Adds the given flag to the internal value.
/// </summary>
/// <param name="flag"></param>
/// <returns>True if the flag has been added. (false if it was already present)</returns>
public bool Add(T flag)
{
int value = IntValue;
int flagToAdd = ToInt(flag);
int newValue = value | flagToAdd;
m_value = (T)(object)newValue;
return value != newValue;
}
/// <summary>
/// True if the flag contains that given value.
/// </summary>
/// <param name="value">Value to test for</param>
/// <returns>True if the flag contains that given value.</returns>
public bool Contains(T value)
{
int flag = ToInt(value);
return (IntValue & flag) == flag;
}
/// <summary>
/// True if the flag contains EVERY values.
/// </summary>
/// <param name="value">First value to test for</param>
/// <param name="otherValues">Additional values to test for.</param>
/// <returns>True if the flag contains EVERY values.</returns>
public bool Contains(T value, params T[] otherValues)
{
if (!Contains(value))
{
return false;
}
foreach (T t in otherValues)
{
if (!Contains(t))
{
return false;
}
}
return true;
}
/// <summary>
/// True if any of the given values are contained in the flag.
/// </summary>
/// <param name="value">First value to test for</param>
/// <param name="otherValues">Additional values to test for.</param>
/// <returns>True if any of the given values are contained in the flag.</returns>
public bool ContainsAny(T value, params T[] otherValues)
{
if (Contains(value))
{
return true;
}
foreach (T other in otherValues)
{
if (Contains(other))
{
return true;
}
}
return false;
}
/// <summary>
/// True if the flag has any overlap with the given <paramref name="value"/>.
/// </summary>
/// <param name="value">Value to test for.</param>
/// <returns>True if the flag has any overlap with the given <paramref name="value"/>.</returns>
public bool Intersects(T value)
{
int flag = ToInt(value);
return (IntValue & flag) != 0;
}
#endregion
#region Comparison
/// <summary>
/// True if the enum equals our value.
/// </summary>
/// <param name="other"></param>
/// <returns></returns>
public bool Equals(T other)
{
return Equals(m_value, other);
}
/// <summary>
/// True if the enum equals our value.
/// </summary>
public override bool Equals(object obj)
{
if (obj is Flags<T>)
{
return Equals(((Flags<T>)obj).m_value, m_value);
}
return Equals(m_value, obj);
}
/// <summary>
/// Hashcode.
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return m_value.GetHashCode();
}
/// <summary>
/// True if the flag value is any of the given values.
/// </summary>
/// <param name="first"></param>
/// <param name="other"></param>
/// <returns>True if the flag value is any of the given values.</returns>
public bool EqualsAny(T first, params T[] other)
{
if (Equals(first))
{
return true;
}
return other.Any(Equals);
}
#endregion
#region Operators
/// <summary>
/// == Operator.
/// </summary>
public static bool operator ==(Flags<T> a, Flags<T> b)
{
return Equals((T)a, (T)b);
}
/// <summary>
/// != Operator.
/// </summary>
public static bool operator !=(Flags<T> a, Flags<T> b)
{
return !Equals((T)a, (T)b);
}
/// <summary>
/// Implicit cast Operator.
/// </summary>
public static implicit operator T(Flags<T> value)
{
return value.m_value;
}
/// <summary>
/// Implicit cast Operator.
/// </summary>
public static implicit operator Flags<T>(T value)
{
return new Flags<T>(value);
}
public static Flags<T> operator |(Flags<T> a, Flags<T> b)
{
a.Add(b);
return a;
}
#endregion
#region ToString
/// <summary>
/// Returns the display of the inner Value.
/// </summary>
public override string ToString()
{
return $"Flags '{m_value}'";
}
#endregion
#region Utilities
/// <summary>
/// All the defined flags defined in the enum.
/// </summary>
public static IEnumerable<T> Values
{
get { return Enum.GetValues(typeof (T)).Cast<T>(); }
}
#endregion
}
}