Search code examples
c#reflectionenumssystem.reflection

Create a delegate which returns enum's underlying int, without knowing the enum type at runtime


I am writing a settings system which relies on adding attributes to properties, and then using reflection.

For example, I create sliders by adding a SliderAttribute to float properties, then finding all those attributes and creating delegates to modify the associated property like so:

Func<float> getterDelegate = Delegate.CreateDelegate(typeof(Func<float>), arg, property.GetGetMethod()) as Func<float>;
Action<float> setterDelegate = Delegate.CreateDelegate(typeof(Action<float>), arg, property.GetSetMethod()) as Action<float>;

settingObj = new Slider(sliderAttribute, getterDelegate, setterDelegate);

Now, I'd like to create multiple choice objects by applying the same logic to enum values. That is I want to generate getter/setter delegates which modify the enum property via the underlying type (which we can assume is always int.)

The ideal would be the following, which returns the error ArgumentException: method return type is incompatible. Same result if I use the 'Enum' type.

Func<int> getterDelegate = Delegate.CreateDelegate(typeof(Func<int>), arg, property.GetGetMethod()) as Func<int>;
Action<int> setterDelegate = Delegate.CreateDelegate(typeof(Action<int>), arg, property.GetSetMethod()) as Action<int>;

settingObj = new MultipleChoice(multipleChoiceAttribute, getterDelegate, setterDelegate, property.PropertyType);

Solution

  • You can create delegates that return and take the actual enum type, like this:

    Delegate getterEnumDelegate = Delegate.CreateDelegate(
        typeof(Func<>).MakeGenericType(property.PropertyType), arg, property.GetGetMethod()
    );
    Delegate setterEnumDelegate = Delegate.CreateDelegate(
        typeof(Action<>).MakeGenericType(property.PropertyType), arg, property.GetSetMethod()
    );
    

    To convert these into Action<int> and Func<int>, you just need to do:

    Func<int> getterDelegate = () => (int)getterEnumDelegate.DynamicInvoke();
    Action<int> setterDelegate = x => setterEnumDelegate.DynamicInvoke(x);