2021-06-11 20:05:32 +02:00
using System ;
using System.Collections.Generic ;
2021-10-28 22:14:43 +02:00
using System.Globalization ;
2021-06-11 20:05:32 +02:00
using System.Linq ;
using System.Numerics ;
using System.Reflection ;
using MLEM.Data.Json ;
using Newtonsoft.Json ;
namespace MLEM.Data {
/// <summary>
/// A dynamic enum is a class that represents enum-like single-instance value behavior with additional capabilities, including dynamic addition of new arbitrary values.
/// A dynamic enum uses <see cref="BigInteger"/> as its underlying type, allowing for an arbitrary number of enum values to be created, even when a <see cref="FlagsAttribute"/>-like structure is used that would only allow for up to 64 values in a regular enum.
/// All enum operations including <see cref="And{T}"/>, <see cref="Or{T}"/>, <see cref="Xor{T}"/> and <see cref="Neg{T}"/> are supported and can be implemented in derived classes using operator overloads.
/// To create a custom dynamic enum, simply create a class that extends <see cref="DynamicEnum"/>. New values can then be added using <see cref="Add{T}"/>, <see cref="AddValue{T}"/> or <see cref="AddFlag{T}"/>.
/// </summary>
/// <remarks>
/// To include enum-like operator overloads in a dynamic enum named MyEnum, the following code can be used:
/// <code>
/// public static implicit operator BigInteger(MyEnum value) => GetValue(value);
/// public static implicit operator MyEnum(BigInteger value) => GetEnumValue<MyEnum>(value);
/// public static MyEnum operator |(MyEnum left, MyEnum right) => Or(left, right);
/// public static MyEnum operator &(MyEnum left, MyEnum right) => And(left, right);
/// public static MyEnum operator ^(MyEnum left, MyEnum right) => Xor(left, right);
/// public static MyEnum operator ~(MyEnum value) => Neg(value);
/// </code>
/// </remarks>
2022-10-31 13:20:26 +01:00
[Obsolete("DynamicEnum has been moved into the DynamicEnums library: https://www.nuget.org/packages/DynamicEnums"), JsonConverter(typeof(DynamicEnumConverter))]
2022-10-31 18:33:53 +01:00
#if NET6_0_OR_GREATER
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Aot", "IL2067")]
#endif
2021-06-11 20:05:32 +02:00
public abstract class DynamicEnum {
2021-09-05 16:18:26 +02:00
private static readonly Dictionary < Type , Storage > Storages = new Dictionary < Type , Storage > ( ) ;
2021-06-11 20:05:32 +02:00
private readonly BigInteger value ;
2021-06-11 20:22:25 +02:00
private Dictionary < DynamicEnum , bool > allFlagsCache ;
private Dictionary < DynamicEnum , bool > anyFlagsCache ;
2021-06-11 20:05:32 +02:00
private string name ;
/// <summary>
/// Creates a new dynamic enum instance.
/// This constructor is protected as it is only invoked via reflection.
/// </summary>
/// <param name="name">The name of the enum value</param>
/// <param name="value">The value</param>
protected DynamicEnum ( string name , BigInteger value ) {
this . value = value ;
this . name = name ;
}
/// <summary>
2021-06-11 20:22:25 +02:00
/// Returns true if this enum value has ALL of the given <see cref="DynamicEnum"/> flags on it.
2021-06-11 20:05:32 +02:00
/// This operation is equivalent to <see cref="Enum.HasFlag"/>.
/// </summary>
2021-06-11 20:22:25 +02:00
/// <seealso cref="HasAnyFlag"/>
/// <param name="flags">The flags to query</param>
/// <returns>True if all of the flags are present, false otherwise</returns>
public bool HasFlag ( DynamicEnum flags ) {
if ( this . allFlagsCache = = null )
this . allFlagsCache = new Dictionary < DynamicEnum , bool > ( ) ;
if ( ! this . allFlagsCache . TryGetValue ( flags , out var ret ) ) {
2022-06-15 11:38:11 +02:00
ret = ( DynamicEnum . GetValue ( this ) & DynamicEnum . GetValue ( flags ) ) = = DynamicEnum . GetValue ( flags ) ;
2021-06-11 20:22:25 +02:00
this . allFlagsCache . Add ( flags , ret ) ;
}
return ret ;
}
/// <summary>
/// Returns true if this enum value has ANY of the given <see cref="DynamicEnum"/> flags on it
/// </summary>
/// <seealso cref="HasFlag"/>
/// <param name="flags">The flags to query</param>
/// <returns>True if one of the flags is present, false otherwise</returns>
public bool HasAnyFlag ( DynamicEnum flags ) {
if ( this . anyFlagsCache = = null )
this . anyFlagsCache = new Dictionary < DynamicEnum , bool > ( ) ;
if ( ! this . anyFlagsCache . TryGetValue ( flags , out var ret ) ) {
2022-06-15 11:38:11 +02:00
ret = ( DynamicEnum . GetValue ( this ) & DynamicEnum . GetValue ( flags ) ) ! = 0 ;
2021-06-11 20:22:25 +02:00
this . anyFlagsCache . Add ( flags , ret ) ;
}
return ret ;
2021-06-11 20:05:32 +02:00
}
2021-11-22 19:25:18 +01:00
/// <summary>Returns a string that represents the current object.</summary>
/// <returns>A string that represents the current object.</returns>
2021-06-11 20:05:32 +02:00
public override string ToString ( ) {
if ( this . name = = null ) {
var included = new List < DynamicEnum > ( ) ;
2022-06-15 11:38:11 +02:00
if ( DynamicEnum . GetValue ( this ) ! = 0 ) {
foreach ( var v in DynamicEnum . GetValues ( this . GetType ( ) ) ) {
if ( this . HasFlag ( v ) & & DynamicEnum . GetValue ( v ) ! = 0 )
2021-06-11 20:05:32 +02:00
included . Add ( v ) ;
}
}
2022-06-15 11:38:11 +02:00
this . name = included . Count > 0 ? string . Join ( " | " , included ) : DynamicEnum . GetValue ( this ) . ToString ( ) ;
2021-06-11 20:05:32 +02:00
}
return this . name ;
}
/// <summary>
/// Adds a new enum value to the given enum type <typeparamref name="T"/>
/// </summary>
/// <param name="name">The name of the enum value to add</param>
/// <param name="value">The value to add</param>
/// <typeparam name="T">The type to add this value to</typeparam>
/// <returns>The newly created enum value</returns>
/// <exception cref="ArgumentException">Thrown if the name or value passed are already present</exception>
public static T Add < T > ( string name , BigInteger value ) where T : DynamicEnum {
2022-06-15 11:38:11 +02:00
var storage = DynamicEnum . GetStorage ( typeof ( T ) ) ;
2021-06-11 20:05:32 +02:00
// cached parsed values and names might be incomplete with new values
2021-09-05 16:18:26 +02:00
storage . ClearCaches ( ) ;
2021-06-11 20:05:32 +02:00
2021-09-05 16:18:26 +02:00
if ( storage . Values . ContainsKey ( value ) )
2021-06-11 20:05:32 +02:00
throw new ArgumentException ( $"Duplicate value {value}" , nameof ( value ) ) ;
2021-09-05 16:18:26 +02:00
foreach ( var v in storage . Values . Values ) {
2021-07-18 22:18:46 +02:00
if ( v . name = = name )
throw new ArgumentException ( $"Duplicate name {name}" , nameof ( name ) ) ;
}
2021-06-11 20:05:32 +02:00
2022-06-15 11:38:11 +02:00
var ret = DynamicEnum . Construct ( typeof ( T ) , name , value ) ;
2021-09-05 16:18:26 +02:00
storage . Values . Add ( value , ret ) ;
2021-06-11 20:05:32 +02:00
return ( T ) ret ;
}
/// <summary>
/// Adds a new enum value to the given enum type <typeparamref name="T"/>.
/// This method differs from <see cref="Add{T}"/> in that it automatically determines a value.
/// The value determined will be the next free number in a sequence, which represents the default behavior in an enum if enum values are not explicitly numbered.
/// </summary>
/// <param name="name">The name of the enum value to add</param>
/// <typeparam name="T">The type to add this value to</typeparam>
/// <returns>The newly created enum value</returns>
public static T AddValue < T > ( string name ) where T : DynamicEnum {
BigInteger value = 0 ;
2022-10-26 23:34:30 +02:00
while ( DynamicEnum . IsDefined ( typeof ( T ) , value ) )
2021-09-05 16:18:26 +02:00
value + + ;
2022-06-15 11:38:11 +02:00
return DynamicEnum . Add < T > ( name , value ) ;
2021-06-11 20:05:32 +02:00
}
/// <summary>
/// Adds a new flag enum value to the given enum type <typeparamref name="T"/>.
/// This method differs from <see cref="Add{T}"/> in that it automatically determines a value.
/// The value determined will be the next free power of two, allowing enum values to be combined using bitwise operations to create <see cref="FlagsAttribute"/>-like behavior.
/// </summary>
/// <param name="name">The name of the enum value to add</param>
/// <typeparam name="T">The type to add this value to</typeparam>
/// <returns>The newly created enum value</returns>
public static T AddFlag < T > ( string name ) where T : DynamicEnum {
2021-09-22 21:32:34 +02:00
BigInteger value = 1 ;
2022-10-26 23:34:30 +02:00
while ( DynamicEnum . IsDefined ( typeof ( T ) , value ) )
2021-09-22 21:32:34 +02:00
value < < = 1 ;
2022-06-15 11:38:11 +02:00
return DynamicEnum . Add < T > ( name , value ) ;
2021-06-11 20:05:32 +02:00
}
/// <summary>
/// Returns a collection of all of the enum values that are explicitly defined for the given dynamic enum type <typeparamref name="T"/>.
/// A value counts as explicitly defined if it has been added using <see cref="Add{T}"/>, <see cref="AddValue{T}"/> or <see cref="AddFlag{T}"/>.
/// </summary>
/// <typeparam name="T">The type whose values to get</typeparam>
/// <returns>The defined values for the given type</returns>
public static IEnumerable < T > GetValues < T > ( ) where T : DynamicEnum {
2022-06-15 11:38:11 +02:00
return DynamicEnum . GetValues ( typeof ( T ) ) . Cast < T > ( ) ;
2021-06-11 20:05:32 +02:00
}
/// <summary>
/// Returns a collection of all of the enum values that are explicitly defined for the given dynamic enum type <paramref name="type"/>.
/// A value counts as explicitly defined if it has been added using <see cref="Add{T}"/>, <see cref="AddValue{T}"/> or <see cref="AddFlag{T}"/>.
/// </summary>
/// <param name="type">The type whose values to get</param>
/// <returns>The defined values for the given type</returns>
public static IEnumerable < DynamicEnum > GetValues ( Type type ) {
2022-06-15 11:38:11 +02:00
return DynamicEnum . GetStorage ( type ) . Values . Values ;
2021-06-11 20:05:32 +02:00
}
2022-10-26 15:02:33 +02:00
/// <summary>
/// Returns all of the defined values from the given dynamic enum type <typeparamref name="T"/> which are contained in <paramref name="combinedFlag"/>.
/// Note that, if combined flags are defined in <typeparamref name="T"/>, and <paramref name="combinedFlag"/> contains them, they will also be returned.
/// </summary>
/// <param name="combinedFlag">The combined flags whose individual flags to return.</param>
/// <param name="includeZero">Whether the enum value 0 should also be returned, if <typeparamref name="T"/> contains one.</param>
/// <typeparam name="T">The type of enum.</typeparam>
/// <returns>All of the flags that make up <paramref name="combinedFlag"/>.</returns>
public static IEnumerable < T > GetFlags < T > ( T combinedFlag , bool includeZero = true ) where T : DynamicEnum {
foreach ( var flag in DynamicEnum . GetValues < T > ( ) ) {
if ( combinedFlag . HasFlag ( flag ) & & ( includeZero | | DynamicEnum . GetValue ( flag ) ! = BigInteger . Zero ) )
yield return flag ;
}
}
2022-10-26 23:34:30 +02:00
/// <summary>
/// Returns all of the defined unique flags from the given dynamic enum type <typeparamref name="T"/> which are contained in <paramref name="combinedFlag"/>.
/// Any combined flags (flags that aren't powers of two) which are defined in <typeparamref name="T"/> will not be returned.
/// </summary>
/// <param name="combinedFlag">The combined flags whose individual flags to return.</param>
/// <typeparam name="T">The type of enum.</typeparam>
/// <returns>All of the unique flags that make up <paramref name="combinedFlag"/>.</returns>
public static IEnumerable < T > GetUniqueFlags < T > ( T combinedFlag ) where T : DynamicEnum {
// we can't use the same method here as EnumHelper.GetUniqueFlags since DynamicEnum doesn't guarantee sorted values
var max = DynamicEnum . GetValues < T > ( ) . Max ( DynamicEnum . GetValue ) ;
var uniqueFlag = BigInteger . One ;
while ( uniqueFlag < = max ) {
if ( DynamicEnum . IsDefined ( typeof ( T ) , uniqueFlag ) ) {
var uniqueFlagValue = DynamicEnum . GetEnumValue < T > ( uniqueFlag ) ;
if ( combinedFlag . HasFlag ( uniqueFlagValue ) )
yield return uniqueFlagValue ;
}
uniqueFlag < < = 1 ;
}
}
2021-06-11 20:05:32 +02:00
/// <summary>
/// Returns the bitwise OR (|) combination of the two dynamic enum values
/// </summary>
/// <param name="left">The left value</param>
/// <param name="right">The right value</param>
/// <typeparam name="T">The type of the values</typeparam>
/// <returns>The bitwise OR (|) combination</returns>
public static T Or < T > ( T left , T right ) where T : DynamicEnum {
2022-06-15 11:38:11 +02:00
var cache = DynamicEnum . GetStorage ( typeof ( T ) ) . OrCache ;
2021-09-05 16:18:26 +02:00
if ( ! cache . TryGetValue ( ( left , right ) , out var ret ) ) {
2022-06-15 11:38:11 +02:00
ret = DynamicEnum . GetEnumValue < T > ( DynamicEnum . GetValue ( left ) | DynamicEnum . GetValue ( right ) ) ;
2021-09-05 16:18:26 +02:00
cache . Add ( ( left , right ) , ret ) ;
}
return ( T ) ret ;
2021-06-11 20:05:32 +02:00
}
/// <summary>
/// Returns the bitwise AND (&) combination of the two dynamic enum values
/// </summary>
/// <param name="left">The left value</param>
/// <param name="right">The right value</param>
/// <typeparam name="T">The type of the values</typeparam>
/// <returns>The bitwise AND (&) combination</returns>
public static T And < T > ( T left , T right ) where T : DynamicEnum {
2022-06-15 11:38:11 +02:00
var cache = DynamicEnum . GetStorage ( typeof ( T ) ) . AndCache ;
2021-09-05 16:18:26 +02:00
if ( ! cache . TryGetValue ( ( left , right ) , out var ret ) ) {
2022-06-15 11:38:11 +02:00
ret = DynamicEnum . GetEnumValue < T > ( DynamicEnum . GetValue ( left ) & DynamicEnum . GetValue ( right ) ) ;
2021-09-05 16:18:26 +02:00
cache . Add ( ( left , right ) , ret ) ;
}
return ( T ) ret ;
2021-06-11 20:05:32 +02:00
}
/// <summary>
/// Returns the bitwise XOR (^) combination of the two dynamic enum values
/// </summary>
/// <param name="left">The left value</param>
/// <param name="right">The right value</param>
/// <typeparam name="T">The type of the values</typeparam>
/// <returns>The bitwise XOR (^) combination</returns>
public static T Xor < T > ( T left , T right ) where T : DynamicEnum {
2022-06-15 11:38:11 +02:00
var cache = DynamicEnum . GetStorage ( typeof ( T ) ) . XorCache ;
2021-09-05 16:18:26 +02:00
if ( ! cache . TryGetValue ( ( left , right ) , out var ret ) ) {
2022-06-15 11:38:11 +02:00
ret = DynamicEnum . GetEnumValue < T > ( DynamicEnum . GetValue ( left ) ^ DynamicEnum . GetValue ( right ) ) ;
2021-09-05 16:18:26 +02:00
cache . Add ( ( left , right ) , ret ) ;
}
return ( T ) ret ;
2021-06-11 20:05:32 +02:00
}
/// <summary>
/// Returns the bitwise NEG (~) combination of the dynamic enum value
/// </summary>
/// <param name="value">The value</param>
/// <typeparam name="T">The type of the values</typeparam>
/// <returns>The bitwise NEG (~) value</returns>
public static T Neg < T > ( T value ) where T : DynamicEnum {
2022-06-15 11:38:11 +02:00
var cache = DynamicEnum . GetStorage ( typeof ( T ) ) . NegCache ;
2021-09-05 16:18:26 +02:00
if ( ! cache . TryGetValue ( value , out var ret ) ) {
2022-06-15 11:38:11 +02:00
ret = DynamicEnum . GetEnumValue < T > ( ~ DynamicEnum . GetValue ( value ) ) ;
2021-09-05 16:18:26 +02:00
cache . Add ( value , ret ) ;
}
return ( T ) ret ;
2021-06-11 20:05:32 +02:00
}
/// <summary>
/// Returns the <see cref="BigInteger"/> representation of the given dynamic enum value
/// </summary>
/// <param name="value">The value whose number representation to get</param>
/// <returns>The value's number representation</returns>
public static BigInteger GetValue ( DynamicEnum value ) {
return value ? . value ? ? 0 ;
}
/// <summary>
/// Returns the defined or combined dynamic enum value for the given <see cref="BigInteger"/> representation
/// </summary>
/// <param name="value">The value whose dynamic enum value to get</param>
/// <typeparam name="T">The type that the returned dynamic enum should have</typeparam>
/// <returns>The defined or combined dynamic enum value</returns>
public static T GetEnumValue < T > ( BigInteger value ) where T : DynamicEnum {
2022-06-15 11:38:11 +02:00
return ( T ) DynamicEnum . GetEnumValue ( typeof ( T ) , value ) ;
2021-06-11 20:05:32 +02:00
}
/// <summary>
/// Returns the defined or combined dynamic enum value for the given <see cref="BigInteger"/> representation
/// </summary>
/// <param name="type">The type that the returned dynamic enum should have</param>
/// <param name="value">The value whose dynamic enum value to get</param>
/// <returns>The defined or combined dynamic enum value</returns>
public static DynamicEnum GetEnumValue ( Type type , BigInteger value ) {
2022-06-15 11:38:11 +02:00
var storage = DynamicEnum . GetStorage ( type ) ;
2021-09-05 16:18:26 +02:00
2021-06-11 20:05:32 +02:00
// get the defined value if it exists
2021-09-05 16:18:26 +02:00
if ( storage . Values . TryGetValue ( value , out var defined ) )
2021-06-11 20:05:32 +02:00
return defined ;
// otherwise, cache the combined value
2021-09-05 16:18:26 +02:00
if ( ! storage . FlagCache . TryGetValue ( value , out var combined ) ) {
2022-06-15 11:38:11 +02:00
combined = DynamicEnum . Construct ( type , null , value ) ;
2021-09-05 16:18:26 +02:00
storage . FlagCache . Add ( value , combined ) ;
2021-06-11 20:05:32 +02:00
}
return combined ;
}
/// <summary>
/// Parses the given <see cref="string"/> into a dynamic enum value and returns the result.
/// This method supports defined enum values as well as values combined using the pipe (|) character and any number of spaces.
/// If no enum value can be parsed, null is returned.
/// </summary>
/// <param name="strg">The string to parse into a dynamic enum value</param>
/// <typeparam name="T">The type of the dynamic enum value to parse</typeparam>
/// <returns>The parsed enum value, or null if parsing fails</returns>
public static T Parse < T > ( string strg ) where T : DynamicEnum {
2022-06-15 11:38:11 +02:00
return ( T ) DynamicEnum . Parse ( typeof ( T ) , strg ) ;
2021-06-11 20:05:32 +02:00
}
/// <summary>
/// Parses the given <see cref="string"/> into a dynamic enum value and returns the result.
/// This method supports defined enum values as well as values combined using the pipe (|) character and any number of spaces.
2022-10-26 23:34:30 +02:00
/// If no enum value can be parsed, null is returned.
/// </summary>
2021-06-11 20:05:32 +02:00
/// <param name="type">The type of the dynamic enum value to parse</param>
/// <param name="strg">The string to parse into a dynamic enum value</param>
/// <returns>The parsed enum value, or null if parsing fails</returns>
public static DynamicEnum Parse ( Type type , string strg ) {
2022-06-15 11:38:11 +02:00
var cache = DynamicEnum . GetStorage ( type ) . ParseCache ;
2021-06-11 20:05:32 +02:00
if ( ! cache . TryGetValue ( strg , out var cached ) ) {
BigInteger ? accum = null ;
foreach ( var val in strg . Split ( '|' ) ) {
2022-06-15 11:38:11 +02:00
foreach ( var defined in DynamicEnum . GetValues ( type ) ) {
2021-06-11 20:05:32 +02:00
if ( defined . name = = val . Trim ( ) ) {
2022-06-15 11:38:11 +02:00
accum = ( accum ? ? 0 ) | DynamicEnum . GetValue ( defined ) ;
2021-06-11 20:05:32 +02:00
break ;
}
}
}
if ( accum ! = null )
2022-06-15 11:38:11 +02:00
cached = DynamicEnum . GetEnumValue ( type , accum . Value ) ;
2021-06-11 20:05:32 +02:00
cache . Add ( strg , cached ) ;
}
return cached ;
}
2022-10-26 23:34:30 +02:00
/// <summary>
/// Returns whether the given <paramref name="value"/> is defined in the given dynamic enum <paramref name="type"/>.
/// A value counts as explicitly defined if it has been added using <see cref="Add{T}"/>, <see cref="AddValue{T}"/> or <see cref="AddFlag{T}"/>.
/// </summary>
/// <param name="type">The dynamic enum type to query.</param>
/// <param name="value">The value to query.</param>
/// <returns>Whether the <paramref name="value"/> is defined.</returns>
public static bool IsDefined ( Type type , BigInteger value ) {
return DynamicEnum . GetStorage ( type ) . Values . ContainsKey ( value ) ;
}
/// <summary>
/// Returns whether the given <paramref name="value"/> is defined in its dynamic enum type.
/// A value counts as explicitly defined if it has been added using <see cref="Add{T}"/>, <see cref="AddValue{T}"/> or <see cref="AddFlag{T}"/>.
/// </summary>
/// <param name="value">The value to query.</param>
/// <returns>Whether the <paramref name="value"/> is defined.</returns>
public static bool IsDefined ( DynamicEnum value ) {
return value ! = null & & DynamicEnum . IsDefined ( value . GetType ( ) , DynamicEnum . GetValue ( value ) ) ;
}
2021-09-05 16:18:26 +02:00
private static Storage GetStorage ( Type type ) {
2022-06-15 11:38:11 +02:00
if ( ! DynamicEnum . Storages . TryGetValue ( type , out var storage ) ) {
2021-09-05 16:18:26 +02:00
storage = new Storage ( ) ;
2022-06-15 11:38:11 +02:00
DynamicEnum . Storages . Add ( type , storage ) ;
2021-09-05 16:18:26 +02:00
}
return storage ;
}
2021-06-11 20:05:32 +02:00
private static DynamicEnum Construct ( Type type , string name , BigInteger value ) {
2021-10-28 22:14:43 +02:00
return ( DynamicEnum ) Activator . CreateInstance ( type , BindingFlags . Instance | BindingFlags . Public | BindingFlags . NonPublic , null , new object [ ] { name , value } , CultureInfo . InvariantCulture ) ;
2021-06-11 20:05:32 +02:00
}
2021-09-05 16:18:26 +02:00
private class Storage {
public readonly Dictionary < BigInteger , DynamicEnum > Values = new Dictionary < BigInteger , DynamicEnum > ( ) ;
public readonly Dictionary < BigInteger , DynamicEnum > FlagCache = new Dictionary < BigInteger , DynamicEnum > ( ) ;
public readonly Dictionary < string , DynamicEnum > ParseCache = new Dictionary < string , DynamicEnum > ( ) ;
public readonly Dictionary < ( DynamicEnum , DynamicEnum ) , DynamicEnum > OrCache = new Dictionary < ( DynamicEnum , DynamicEnum ) , DynamicEnum > ( ) ;
public readonly Dictionary < ( DynamicEnum , DynamicEnum ) , DynamicEnum > AndCache = new Dictionary < ( DynamicEnum , DynamicEnum ) , DynamicEnum > ( ) ;
public readonly Dictionary < ( DynamicEnum , DynamicEnum ) , DynamicEnum > XorCache = new Dictionary < ( DynamicEnum , DynamicEnum ) , DynamicEnum > ( ) ;
public readonly Dictionary < DynamicEnum , DynamicEnum > NegCache = new Dictionary < DynamicEnum , DynamicEnum > ( ) ;
public void ClearCaches ( ) {
this . FlagCache . Clear ( ) ;
this . ParseCache . Clear ( ) ;
this . OrCache . Clear ( ) ;
this . AndCache . Clear ( ) ;
this . XorCache . Clear ( ) ;
this . NegCache . Clear ( ) ;
}
}
2021-06-11 20:05:32 +02:00
}
2022-06-17 18:23:47 +02:00
}