From e17ebf99730913f83a4b664126c389de3f47cc2c Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Sat, 29 Jun 2024 00:08:59 +0200 Subject: [PATCH] allow dynamic enum classes to use a parameterless constructor --- DynamicEnums/DynamicEnum.cs | 25 ++++++++++++++++++++++--- README.md | 3 --- Tests/EnumTests.cs | 9 +++++++-- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/DynamicEnums/DynamicEnum.cs b/DynamicEnums/DynamicEnum.cs index fb30ddb..82acad7 100644 --- a/DynamicEnums/DynamicEnum.cs +++ b/DynamicEnums/DynamicEnum.cs @@ -30,13 +30,22 @@ namespace DynamicEnums { /// public abstract class DynamicEnum { + private const BindingFlags ConstructorFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + private static readonly Type[] ConstructorTypes = {typeof(string), typeof(BigInteger), typeof(bool)}; private static readonly Dictionary Storages = new Dictionary(); - private readonly BigInteger value; private Dictionary allFlagsCache; private Dictionary anyFlagsCache; + private BigInteger value; private string name; + /// + /// Creates a new dynamic enum instance. + /// This constructor is protected as it is only invoked via reflection. + /// This constructor is only called if the class doesn't have the constructor. + /// + protected DynamicEnum() {} + /// /// Creates a new dynamic enum instance. /// This constructor is protected as it is only invoked via reflection. @@ -45,8 +54,8 @@ namespace DynamicEnums { /// The value /// Whether this enum value , and thus, not a combined flag. protected DynamicEnum(string name, BigInteger value, bool defined) { - this.value = value; this.name = name; + this.value = value; } /// @@ -445,7 +454,17 @@ namespace DynamicEnums { [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] #endif Type type, string name, BigInteger value, bool defined) { - return (DynamicEnum) Activator.CreateInstance(type, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new object[] {name, value, defined}, CultureInfo.InvariantCulture); + // try to call the constructor with parameters first + var parameterConstructor = type.GetConstructor(DynamicEnum.ConstructorFlags, null, DynamicEnum.ConstructorTypes, null); + if (parameterConstructor != null) + return (DynamicEnum) parameterConstructor.Invoke(DynamicEnum.ConstructorFlags, null, new object[] {name, value, defined}, CultureInfo.InvariantCulture); + + // default to the empty constructor and set the values manually + var emptyConstructor = type.GetConstructor(DynamicEnum.ConstructorFlags, null, Type.EmptyTypes, null); + var ret = (DynamicEnum) emptyConstructor.Invoke(DynamicEnum.ConstructorFlags, null, null, CultureInfo.InvariantCulture); + ret.name = name; + ret.value = value; + return ret; } private class Storage { diff --git a/README.md b/README.md index 541309f..e1fb555 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,6 @@ public class MyEnum : DynamicEnum { public static readonly MyEnum FlagTwo = DynamicEnum.AddFlag("FlagTwo"); public static readonly MyEnum FlagThree = DynamicEnum.AddFlag("FlagThree"); - // this constructor is called internally using reflection - public MyEnum(string name, BigInteger value) : base(name, value) {} - // you can optionally create operator overloads for easier operations public static implicit operator BigInteger(MyEnum value) => DynamicEnum.GetValue(value); public static implicit operator MyEnum(BigInteger value) => DynamicEnum.GetEnumValue(value); diff --git a/Tests/EnumTests.cs b/Tests/EnumTests.cs index 79dbea9..e0635c2 100644 --- a/Tests/EnumTests.cs +++ b/Tests/EnumTests.cs @@ -35,6 +35,9 @@ public class EnumTests { var zero = DynamicEnum.Add("Zero", 0); var combined = DynamicEnum.Add("Combined", DynamicEnum.GetValue(DynamicEnum.Or(flags[7], flags[13]))); + DynamicEnum.Add("Test", 10); + Assert.AreEqual(DynamicEnum.GetEnumValue(10).ToString(), "TestModified"); + Assert.AreEqual(DynamicEnum.GetValue(flags[7]), BigInteger.One << 7); Assert.AreEqual(DynamicEnum.GetEnumValue(BigInteger.One << 75), flags[75]); @@ -90,9 +93,11 @@ public class EnumTests { } - private class TestDynamicEnum : DynamicEnum { + private class TestDynamicEnum : DynamicEnum; - public TestDynamicEnum(string name, BigInteger value, bool defined) : base(name, value, defined) {} + private class TestEnumWithConstructor : DynamicEnum { + + public TestEnumWithConstructor(string name, BigInteger value, bool defined) : base($"{name}Modified", value, defined) {} }