diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d88dfb9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+.idea
+bin
+obj
+packages
+*.nupkg
+TestResults
\ No newline at end of file
diff --git a/DynamicEnums.sln b/DynamicEnums.sln
new file mode 100644
index 0000000..e8f240f
--- /dev/null
+++ b/DynamicEnums.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamicEnums", "DynamicEnums\DynamicEnums.csproj", "{1657964D-2503-426A-8514-D020660BEE4D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{8E110BC2-38FD-404A-B5BD-02C771B0D1D5}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {1657964D-2503-426A-8514-D020660BEE4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1657964D-2503-426A-8514-D020660BEE4D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1657964D-2503-426A-8514-D020660BEE4D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1657964D-2503-426A-8514-D020660BEE4D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8E110BC2-38FD-404A-B5BD-02C771B0D1D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8E110BC2-38FD-404A-B5BD-02C771B0D1D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8E110BC2-38FD-404A-B5BD-02C771B0D1D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8E110BC2-38FD-404A-B5BD-02C771B0D1D5}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/DynamicEnums/DynamicEnum.cs b/DynamicEnums/DynamicEnum.cs
new file mode 100644
index 0000000..dad3182
--- /dev/null
+++ b/DynamicEnums/DynamicEnum.cs
@@ -0,0 +1,404 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Numerics;
+using System.Reflection;
+
+namespace DynamicEnums {
+ ///
+ /// 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 as its underlying type, allowing for an arbitrary number of enum values to be created, even when a -like structure is used that would only allow for up to 64 values in a regular enum.
+ /// All enum operations including , , and are supported and can be implemented in derived classes using operator overloads.
+ /// To create a custom dynamic enum, simply create a class that extends . New values can then be added using , or .
+ ///
+ ///
+ /// To include enum-like operator overloads in a dynamic enum named MyEnum, the following code can be used:
+ ///
+ /// 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);
+ ///
+ ///
+ public abstract class DynamicEnum {
+
+ private static readonly Dictionary Storages = new Dictionary();
+ private readonly BigInteger value;
+
+ private Dictionary allFlagsCache;
+ private Dictionary anyFlagsCache;
+ private string name;
+
+ ///
+ /// Creates a new dynamic enum instance.
+ /// This constructor is protected as it is only invoked via reflection.
+ ///
+ /// The name of the enum value
+ /// The value
+ protected DynamicEnum(string name, BigInteger value) {
+ this.value = value;
+ this.name = name;
+ }
+
+ ///
+ /// Returns true if this enum value has all of the given flags on it.
+ /// This operation is equivalent to .
+ ///
+ ///
+ /// The flags to query
+ /// True if all of the flags are present, false otherwise
+ public bool HasAllFlags(DynamicEnum flags) {
+ if (this.allFlagsCache == null)
+ this.allFlagsCache = new Dictionary();
+ if (!this.allFlagsCache.TryGetValue(flags, out var ret)) {
+ ret = (DynamicEnum.GetValue(this) & DynamicEnum.GetValue(flags)) == DynamicEnum.GetValue(flags);
+ this.allFlagsCache.Add(flags, ret);
+ }
+ return ret;
+ }
+
+ ///
+ /// Returns true if this enum value has any of the given flags on it
+ ///
+ ///
+ /// The flags to query
+ /// True if one of the flags is present, false otherwise
+ public bool HasAnyFlags(DynamicEnum flags) {
+ if (this.anyFlagsCache == null)
+ this.anyFlagsCache = new Dictionary();
+ if (!this.anyFlagsCache.TryGetValue(flags, out var ret)) {
+ ret = (DynamicEnum.GetValue(this) & DynamicEnum.GetValue(flags)) != 0;
+ this.anyFlagsCache.Add(flags, ret);
+ }
+ return ret;
+ }
+
+ /// Returns a string that represents the current object.
+ /// A string that represents the current object.
+ public override string ToString() {
+ if (this.name == null) {
+ var included = new List();
+ if (DynamicEnum.GetValue(this) != 0) {
+ foreach (var v in DynamicEnum.GetValues(this.GetType())) {
+ if (this.HasAllFlags(v) && DynamicEnum.GetValue(v) != 0)
+ included.Add(v);
+ }
+ }
+ this.name = included.Count > 0 ? string.Join(" | ", included) : DynamicEnum.GetValue(this).ToString();
+ }
+ return this.name;
+ }
+
+ ///
+ /// Adds a new enum value to the given enum type
+ ///
+ /// The name of the enum value to add
+ /// The value to add
+ /// The type to add this value to
+ /// The newly created enum value
+ /// Thrown if the name or value passed are already present
+ public static T Add(string name, BigInteger value) where T : DynamicEnum {
+ var storage = DynamicEnum.GetStorage(typeof(T));
+
+ // cached parsed values and names might be incomplete with new values
+ storage.ClearCaches();
+
+ if (storage.Values.ContainsKey(value))
+ throw new ArgumentException($"Duplicate value {value}", nameof(value));
+ foreach (var v in storage.Values.Values) {
+ if (v.name == name)
+ throw new ArgumentException($"Duplicate name {name}", nameof(name));
+ }
+
+ var ret = DynamicEnum.Construct(typeof(T), name, value);
+ storage.Values.Add(value, ret);
+ return (T) ret;
+ }
+
+ ///
+ /// Adds a new enum value to the given enum type .
+ /// This method differs from 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.
+ ///
+ /// The name of the enum value to add
+ /// The type to add this value to
+ /// The newly created enum value
+ public static T AddValue(string name) where T : DynamicEnum {
+ BigInteger value = 0;
+ while (DynamicEnum.IsDefined(typeof(T), value))
+ value++;
+ return DynamicEnum.Add(name, value);
+ }
+
+ ///
+ /// Adds a new flag enum value to the given enum type .
+ /// This method differs from 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 -like behavior.
+ ///
+ /// The name of the enum value to add
+ /// The type to add this value to
+ /// The newly created enum value
+ public static T AddFlag(string name) where T : DynamicEnum {
+ BigInteger value = 1;
+ while (DynamicEnum.IsDefined(typeof(T), value))
+ value <<= 1;
+ return DynamicEnum.Add(name, value);
+ }
+
+ ///
+ /// Returns a collection of all of the enum values that are explicitly defined for the given dynamic enum type .
+ /// A value counts as explicitly defined if it has been added using , or .
+ ///
+ /// The type whose values to get
+ /// The defined values for the given type
+ public static IEnumerable GetValues() where T : DynamicEnum {
+ return DynamicEnum.GetValues(typeof(T)).Cast();
+ }
+
+ ///
+ /// Returns a collection of all of the enum values that are explicitly defined for the given dynamic enum type .
+ /// A value counts as explicitly defined if it has been added using , or .
+ ///
+ /// The type whose values to get
+ /// The defined values for the given type
+ public static IEnumerable GetValues(Type type) {
+ return DynamicEnum.GetStorage(type).Values.Values;
+ }
+
+ ///
+ /// Returns all of the defined values from the given dynamic enum type which are contained in .
+ /// Note that, if combined flags are defined in , and contains them, they will also be returned.
+ ///
+ /// The combined flags whose individual flags to return.
+ /// Whether the enum value 0 should also be returned, if contains one.
+ /// The type of enum.
+ /// All of the flags that make up .
+ public static IEnumerable GetFlags(T combinedFlag, bool includeZero = true) where T : DynamicEnum {
+ foreach (var flag in DynamicEnum.GetValues()) {
+ if (combinedFlag.HasAllFlags(flag) && (includeZero || DynamicEnum.GetValue(flag) != BigInteger.Zero))
+ yield return flag;
+ }
+ }
+
+ ///
+ /// Returns all of the defined unique flags from the given dynamic enum type which are contained in .
+ /// Any combined flags (flags that aren't powers of two) which are defined in will not be returned.
+ ///
+ /// The combined flags whose individual flags to return.
+ /// The type of enum.
+ /// All of the unique flags that make up .
+ public static IEnumerable GetUniqueFlags(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().Max(DynamicEnum.GetValue);
+ var uniqueFlag = BigInteger.One;
+ while (uniqueFlag <= max) {
+ if (DynamicEnum.IsDefined(typeof(T), uniqueFlag)) {
+ var uniqueFlagValue = DynamicEnum.GetEnumValue(uniqueFlag);
+ if (combinedFlag.HasAnyFlags(uniqueFlagValue))
+ yield return uniqueFlagValue;
+ }
+ uniqueFlag <<= 1;
+ }
+ }
+
+ ///
+ /// Returns the bitwise OR (|) combination of the two dynamic enum values
+ ///
+ /// The left value
+ /// The right value
+ /// The type of the values
+ /// The bitwise OR (|) combination
+ public static T Or(T left, T right) where T : DynamicEnum {
+ var cache = DynamicEnum.GetStorage(typeof(T)).OrCache;
+ if (!cache.TryGetValue((left, right), out var ret)) {
+ ret = DynamicEnum.GetEnumValue(DynamicEnum.GetValue(left) | DynamicEnum.GetValue(right));
+ cache.Add((left, right), ret);
+ }
+ return (T) ret;
+ }
+
+ ///
+ /// Returns the bitwise AND (&) combination of the two dynamic enum values
+ ///
+ /// The left value
+ /// The right value
+ /// The type of the values
+ /// The bitwise AND (&) combination
+ public static T And(T left, T right) where T : DynamicEnum {
+ var cache = DynamicEnum.GetStorage(typeof(T)).AndCache;
+ if (!cache.TryGetValue((left, right), out var ret)) {
+ ret = DynamicEnum.GetEnumValue(DynamicEnum.GetValue(left) & DynamicEnum.GetValue(right));
+ cache.Add((left, right), ret);
+ }
+ return (T) ret;
+ }
+
+ ///
+ /// Returns the bitwise XOR (^) combination of the two dynamic enum values
+ ///
+ /// The left value
+ /// The right value
+ /// The type of the values
+ /// The bitwise XOR (^) combination
+ public static T Xor(T left, T right) where T : DynamicEnum {
+ var cache = DynamicEnum.GetStorage(typeof(T)).XorCache;
+ if (!cache.TryGetValue((left, right), out var ret)) {
+ ret = DynamicEnum.GetEnumValue(DynamicEnum.GetValue(left) ^ DynamicEnum.GetValue(right));
+ cache.Add((left, right), ret);
+ }
+ return (T) ret;
+ }
+
+ ///
+ /// Returns the bitwise NEG (~) combination of the dynamic enum value
+ ///
+ /// The value
+ /// The type of the values
+ /// The bitwise NEG (~) value
+ public static T Neg(T value) where T : DynamicEnum {
+ var cache = DynamicEnum.GetStorage(typeof(T)).NegCache;
+ if (!cache.TryGetValue(value, out var ret)) {
+ ret = DynamicEnum.GetEnumValue(~DynamicEnum.GetValue(value));
+ cache.Add(value, ret);
+ }
+ return (T) ret;
+ }
+
+ ///
+ /// Returns the representation of the given dynamic enum value
+ ///
+ /// The value whose number representation to get
+ /// The value's number representation
+ public static BigInteger GetValue(DynamicEnum value) {
+ return value?.value ?? 0;
+ }
+
+ ///
+ /// Returns the defined or combined dynamic enum value for the given representation
+ ///
+ /// The value whose dynamic enum value to get
+ /// The type that the returned dynamic enum should have
+ /// The defined or combined dynamic enum value
+ public static T GetEnumValue(BigInteger value) where T : DynamicEnum {
+ return (T) DynamicEnum.GetEnumValue(typeof(T), value);
+ }
+
+ ///
+ /// Returns the defined or combined dynamic enum value for the given representation
+ ///
+ /// The type that the returned dynamic enum should have
+ /// The value whose dynamic enum value to get
+ /// The defined or combined dynamic enum value
+ public static DynamicEnum GetEnumValue(Type type, BigInteger value) {
+ var storage = DynamicEnum.GetStorage(type);
+
+ // get the defined value if it exists
+ if (storage.Values.TryGetValue(value, out var defined))
+ return defined;
+
+ // otherwise, cache the combined value
+ if (!storage.FlagCache.TryGetValue(value, out var combined)) {
+ combined = DynamicEnum.Construct(type, null, value);
+ storage.FlagCache.Add(value, combined);
+ }
+ return combined;
+ }
+
+ ///
+ /// Parses the given 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.
+ ///
+ /// The string to parse into a dynamic enum value
+ /// The type of the dynamic enum value to parse
+ /// The parsed enum value, or null if parsing fails
+ public static T Parse(string strg) where T : DynamicEnum {
+ return (T) DynamicEnum.Parse(typeof(T), strg);
+ }
+
+ ///
+ /// Parses the given 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.
+ ///
+ /// The type of the dynamic enum value to parse
+ /// The string to parse into a dynamic enum value
+ /// The parsed enum value, or null if parsing fails
+ public static DynamicEnum Parse(Type type, string strg) {
+ var cache = DynamicEnum.GetStorage(type).ParseCache;
+ if (!cache.TryGetValue(strg, out var cached)) {
+ BigInteger? accum = null;
+ foreach (var val in strg.Split('|')) {
+ foreach (var defined in DynamicEnum.GetValues(type)) {
+ if (defined.name == val.Trim()) {
+ accum = (accum ?? 0) | DynamicEnum.GetValue(defined);
+ break;
+ }
+ }
+ }
+ if (accum != null)
+ cached = DynamicEnum.GetEnumValue(type, accum.Value);
+ cache.Add(strg, cached);
+ }
+ return cached;
+ }
+
+ ///
+ /// Returns whether the given is defined in the given dynamic enum .
+ /// A value counts as explicitly defined if it has been added using , or .
+ ///
+ /// The dynamic enum type to query.
+ /// The value to query.
+ /// Whether the is defined.
+ public static bool IsDefined(Type type, BigInteger value) {
+ return DynamicEnum.GetStorage(type).Values.ContainsKey(value);
+ }
+
+ ///
+ /// Returns whether the given is defined in its dynamic enum type.
+ /// A value counts as explicitly defined if it has been added using , or .
+ ///
+ /// The value to query.
+ /// Whether the is defined.
+ public static bool IsDefined(DynamicEnum value) {
+ return value != null && DynamicEnum.IsDefined(value.GetType(), DynamicEnum.GetValue(value));
+ }
+
+ private static Storage GetStorage(Type type) {
+ if (!DynamicEnum.Storages.TryGetValue(type, out var storage)) {
+ storage = new Storage();
+ DynamicEnum.Storages.Add(type, storage);
+ }
+ return storage;
+ }
+
+ private static DynamicEnum Construct(Type type, string name, BigInteger value) {
+ return (DynamicEnum) Activator.CreateInstance(type, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new object[] {name, value}, CultureInfo.InvariantCulture);
+ }
+
+ private class Storage {
+
+ public readonly Dictionary Values = new Dictionary();
+ public readonly Dictionary FlagCache = new Dictionary();
+ public readonly Dictionary ParseCache = new Dictionary();
+ 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 NegCache = new Dictionary();
+
+ public void ClearCaches() {
+ this.FlagCache.Clear();
+ this.ParseCache.Clear();
+ this.OrCache.Clear();
+ this.AndCache.Clear();
+ this.XorCache.Clear();
+ this.NegCache.Clear();
+ }
+
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/DynamicEnums/DynamicEnums.csproj b/DynamicEnums/DynamicEnums.csproj
new file mode 100644
index 0000000..371151e
--- /dev/null
+++ b/DynamicEnums/DynamicEnums.csproj
@@ -0,0 +1,26 @@
+
+
+ net452;netstandard2.0;net6.0
+ true
+
+
+
+ Ellpeck
+ Enum-like single-instance values with additional capabilities, including dynamic addition of new arbitrary values and flags
+ enum enumeration dynamic bigint biginteger long
+ https://github.com/Ellpeck/DynamicEnums
+ https://github.com/Ellpeck/DynamicEnums
+ MIT
+ README.md
+ 0.0.1
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DynamicEnums/EnumHelper.cs b/DynamicEnums/EnumHelper.cs
new file mode 100644
index 0000000..83e43e7
--- /dev/null
+++ b/DynamicEnums/EnumHelper.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Collections.Generic;
+
+namespace DynamicEnums {
+ ///
+ /// A helper class that allows easier usage of regular values, as well as values.
+ ///
+ public static class EnumHelper {
+
+ ///
+ /// Returns an array containing all of the values of the given enum type.
+ /// Note that this method is a version-independent equivalent of .NET 5's Enum.GetValues<TEnum>.
+ ///
+ /// The type whose enum to get
+ /// An enumerable of the values of the enum, in declaration order.
+ public static T[] GetValues() where T : struct, Enum {
+ #if NET6_0_OR_GREATER
+ return Enum.GetValues();
+ #else
+ return (T[]) Enum.GetValues(typeof(T));
+ #endif
+ }
+
+ ///
+ /// Returns true if the given has all of the given flags on it.
+ /// This operation is equivalent to , but doesn't do any additional checks, making it faster.
+ ///
+ ///
+ /// The value to query flags on.
+ /// The flags to query.
+ /// True if all of the flags are present, false otherwise.
+ public static bool HasAllFlags(this T value, T flags) where T : struct, Enum {
+ return (Convert.ToInt64(value) & Convert.ToInt64(flags)) == Convert.ToInt64(flags);
+ }
+
+ ///
+ /// Returns true if the given has any of the given flags on it.
+ ///
+ ///
+ /// The value to query flags on.
+ /// The flags to query.
+ /// True if any of the flags are present, false otherwise.
+ public static bool HasAnyFlags(this T value, T flags) where T : struct, Enum {
+ return (Convert.ToInt64(value) & Convert.ToInt64(flags)) != 0;
+ }
+
+ ///
+ /// Returns all of the defined values from the given enum type which are contained in .
+ /// Note that, if combined flags are defined in , and contains them, they will also be returned.
+ ///
+ /// The combined flags whose individual flags to return.
+ /// Whether the enum value 0 should also be returned, if contains one.
+ /// The type of enum.
+ /// All of the flags that make up .
+ public static IEnumerable GetFlags(T combinedFlag, bool includeZero = true) where T : struct, Enum {
+ foreach (var flag in EnumHelper.GetValues()) {
+ if (combinedFlag.HasAllFlags(flag) && (includeZero || Convert.ToInt64(flag) != 0))
+ yield return flag;
+ }
+ }
+
+ ///
+ /// Returns all of the defined unique flags from the given enum type which are contained in .
+ /// Any combined flags (flags that aren't powers of two) which are defined in will not be returned.
+ ///
+ /// The combined flags whose individual flags to return.
+ /// The type of enum.
+ /// All of the unique flags that make up .
+ public static IEnumerable GetUniqueFlags(T combinedFlag) where T : struct, Enum {
+ var uniqueFlag = 1L;
+ foreach (var flag in EnumHelper.GetValues()) {
+ var flagValue = Convert.ToInt64(flag);
+ // GetValues is always ordered by binary value, so we can be sure that the next flag is bigger than the last
+ while (uniqueFlag < flagValue)
+ uniqueFlag <<= 1;
+ if (flagValue == uniqueFlag && combinedFlag.HasAllFlags(flag))
+ yield return flag;
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Jenkinsfile b/Jenkinsfile
new file mode 100644
index 0000000..f96ffd3
--- /dev/null
+++ b/Jenkinsfile
@@ -0,0 +1,35 @@
+pipeline {
+ agent any
+ stages {
+ stage('Test') {
+ steps {
+ sh 'dotnet test --collect:"XPlat Code Coverage"'
+ }
+ }
+
+ stage('Pack') {
+ steps {
+ sh 'find . -type f -name \\\'*.nupkg\\\' -delete'
+ sh 'dotnet pack --version-suffix ${BUILD_NUMBER}'
+ }
+ }
+
+ stage('Publish') {
+ when {
+ branch 'main'
+ }
+ steps {
+ sh 'dotnet nuget push -s https://nuget.ellpeck.de/v3/index.json **/*.nupkg -k $BAGET -n true'
+ }
+ }
+ }
+ post {
+ always {
+ nunit testResultsPattern: '**/TestResults.xml'
+ cobertura coberturaReportFile: '**/coverage.cobertura.xml'
+ }
+ }
+ environment {
+ BAGET = credentials('3db850d0-e6b5-43d5-b607-d180f4eab676')
+ }
+}
diff --git a/README.md b/README.md
index de39ace..1e96d16 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,2 @@
-# DynamicEnum
-Enum-like single-instance values with additional capabilities, including dynamic addition of new arbitrary values
+# DynamicEnums
+Enum-like single-instance values with additional capabilities, including dynamic addition of new arbitrary values and flags
diff --git a/Tests/EnumTests.cs b/Tests/EnumTests.cs
new file mode 100644
index 0000000..8d5ab6f
--- /dev/null
+++ b/Tests/EnumTests.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Numerics;
+using DynamicEnums;
+using NUnit.Framework;
+
+namespace Tests;
+
+public class EnumTests {
+
+ [Test]
+ public void TestRegularEnums() {
+ Assert.AreEqual(
+ new[] {TestEnum.One, TestEnum.Two, TestEnum.Eight, TestEnum.Sixteen, TestEnum.EightSixteen},
+ EnumHelper.GetFlags(TestEnum.One | TestEnum.Sixteen | TestEnum.Eight | TestEnum.Two));
+
+ Assert.AreEqual(
+ new[] {TestEnum.One, TestEnum.Two, TestEnum.Eight, TestEnum.Sixteen},
+ EnumHelper.GetUniqueFlags(TestEnum.One | TestEnum.Sixteen | TestEnum.Eight | TestEnum.Two));
+
+ Assert.AreEqual(TestEnum.One.HasAnyFlags(TestEnum.Two | TestEnum.One), true);
+ Assert.AreEqual(TestEnum.One.HasAnyFlags(TestEnum.One), true);
+ Assert.AreEqual(TestEnum.One.HasAnyFlags(TestEnum.Two), false);
+ Assert.AreEqual(TestEnum.One.HasAnyFlags((TestEnum) 0), false);
+
+ Assert.AreEqual(TestEnum.One.HasAllFlags(TestEnum.Two | TestEnum.One), TestEnum.One.HasFlag(TestEnum.Two | TestEnum.One));
+ Assert.AreEqual(TestEnum.One.HasAllFlags(TestEnum.One), TestEnum.One.HasFlag(TestEnum.One));
+ Assert.AreEqual(TestEnum.One.HasAllFlags((TestEnum) 0), TestEnum.One.HasFlag((TestEnum) 0));
+ }
+
+ [Test]
+ public void TestDynamicEnums() {
+ var flags = new TestDynamicEnum[100];
+ for (var i = 0; i < flags.Length; i++)
+ flags[i] = DynamicEnum.AddFlag("Flag" + i);
+ var combined = DynamicEnum.Add("Combined", DynamicEnum.GetValue(DynamicEnum.Or(flags[7], flags[13])));
+
+ Assert.AreEqual(DynamicEnum.GetValue(flags[7]), BigInteger.One << 7);
+ Assert.AreEqual(DynamicEnum.GetEnumValue(BigInteger.One << 75), flags[75]);
+
+ Assert.AreEqual(DynamicEnum.GetValue(DynamicEnum.Or(flags[2], flags[17])), BigInteger.One << 2 | BigInteger.One << 17);
+ Assert.AreEqual(DynamicEnum.GetValue(DynamicEnum.And(flags[2], flags[3])), BigInteger.Zero);
+ Assert.AreEqual(DynamicEnum.And(DynamicEnum.Or(flags[24], flags[52]), DynamicEnum.Or(flags[52], flags[75])), flags[52]);
+ Assert.AreEqual(DynamicEnum.Xor(DynamicEnum.Or(flags[85], flags[73]), flags[73]), flags[85]);
+ Assert.AreEqual(DynamicEnum.Xor(DynamicEnum.Or(flags[85], DynamicEnum.Or(flags[73], flags[12])), flags[73]), DynamicEnum.Or(flags[85], flags[12]));
+ Assert.AreEqual(DynamicEnum.GetValue(DynamicEnum.Neg(flags[74])), ~(BigInteger.One << 74));
+
+ Assert.AreEqual(DynamicEnum.Or(flags[24], flags[52]).HasAllFlags(flags[24]), true);
+ Assert.AreEqual(DynamicEnum.Or(flags[24], flags[52]).HasAnyFlags(flags[24]), true);
+ Assert.AreEqual(DynamicEnum.Or(flags[24], flags[52]).HasAllFlags(DynamicEnum.Or(flags[24], flags[26])), false);
+ Assert.AreEqual(DynamicEnum.Or(flags[24], flags[52]).HasAnyFlags(DynamicEnum.Or(flags[24], flags[26])), true);
+ Assert.AreEqual(flags[24].HasAllFlags(DynamicEnum.GetEnumValue(0)), true);
+ Assert.AreEqual(flags[24].HasAnyFlags(DynamicEnum.GetEnumValue(0)), false);
+
+ Assert.AreEqual(DynamicEnum.Parse("Flag24"), flags[24]);
+ Assert.AreEqual(DynamicEnum.Parse("Flag24 | Flag43"), DynamicEnum.Or(flags[24], flags[43]));
+ Assert.AreEqual(flags[24].ToString(), "Flag24");
+ Assert.AreEqual(DynamicEnum.Or(flags[24], flags[43]).ToString(), "Flag24 | Flag43");
+
+ Assert.True(DynamicEnum.IsDefined(flags[27]));
+ Assert.True(DynamicEnum.IsDefined(combined));
+ Assert.False(DynamicEnum.IsDefined(DynamicEnum.Or(flags[17], flags[49])));
+ Assert.False(DynamicEnum.IsDefined(DynamicEnum.Or(combined, flags[49])));
+
+ Assert.AreEqual(
+ new[] {flags[0], flags[7], flags[13], combined},
+ DynamicEnum.GetFlags(DynamicEnum.Or(DynamicEnum.Or(flags[0], flags[13]), flags[7])));
+
+ Assert.AreEqual(
+ new[] {flags[0], flags[7], flags[13]},
+ DynamicEnum.GetUniqueFlags(DynamicEnum.Or(DynamicEnum.Or(flags[0], flags[13]), flags[7])));
+ }
+
+ [Flags]
+ private enum TestEnum {
+
+ One = 1,
+ Two = 2,
+ Eight = 8,
+ Sixteen = 16,
+ EightSixteen = TestEnum.Eight | TestEnum.Sixteen,
+ ThirtyTwo = 32,
+ OneTwentyEight = 128,
+ OneTwentyEightTwoOne = TestEnum.OneTwentyEight | TestEnum.Two | TestEnum.One
+
+ }
+
+ private class TestDynamicEnum : DynamicEnum {
+
+ public TestDynamicEnum(string name, BigInteger value) : base(name, value) {}
+
+ }
+
+}
\ No newline at end of file
diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj
new file mode 100644
index 0000000..d87f80b
--- /dev/null
+++ b/Tests/Tests.csproj
@@ -0,0 +1,20 @@
+
+
+ net6.0
+ nunit
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+