Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C#: Enum.IsDefined on combined flags

I have this enum:

[Flags]
public enum ExportFormat
{
    None = 0,
    Csv = 1,
    Tsv = 2,
    Excel = 4,
    All = Excel | Csv | Tsv
}

I am trying to make a wrapper on this (or any, really) enum which notifies on change. Currently it looks like this:

public class NotifyingEnum<T> : INotifyPropertyChanged
    where T : struct
{
    private T value;

    public event PropertyChangedEventHandler PropertyChanged;

    public NotifyingEnum()
    {
        if (!typeof (T).IsEnum)
            throw new ArgumentException("Type T must be an Enum");
    }

    public T Value
    {
        get { return value; }
        set
        {
            if (!Enum.IsDefined(typeof (T), value))
                throw new ArgumentOutOfRangeException("value", value, "Value not defined in enum, " + typeof (T).Name);

            if (!this.value.Equals(value))
            {
                this.value = value;

                PropertyChangedEventHandler handler = PropertyChanged;
                if (handler != null)
                    handler(this, new PropertyChangedEventArgs("Value"));
            }
        }
    }
}

Since an enum can be assigned with any value really, I want to check if the given Value is defined. But I found a problem. If I here give it an enum consisting of for example Csv | Excel, then Enum.IsDefined will return false. Apparently because I haven't defined any enum consisting of those two. I guess that on some level is logical, but how should I then check if the given value is valid? In other words, to make it work, what do I need to swap this following line with?

if (!Enum.IsDefined(typeof (T), value))
like image 845
Svish Avatar asked Feb 09 '09 09:02

Svish


2 Answers

We know that an enum value converted to a string will never start with a digit, but one that has an invalid value always will. Here's the simplest solution:

public static bool IsDefinedEx(this Enum yourEnum)
{
    char firstDigit = yourEnum.ToString()[0];
    if (Char.IsDigit(firstDigit) || firstDigit == '-')  // Account for signed enums too..
        return false;

    return true;
}

Use that extension method instead of the stock IsDefined and that should solve your issue.

like image 99
Dan McCann Avatar answered Oct 26 '22 14:10

Dan McCann


With flag-based enums, it's about having a bit set or not. So for 'ExportFormat', if bit 1 is set, it's CSV format, even though there might be more bits set. Is having bit 1 and 2 set an invalid value? This is subjective: from the point of view of the values as a group, it is invalid (there's no bitpattern defined for bits 1 and 2 set) however, as each value is a bit, looking at them individually, it can be that a value with bits 1 and 2 set is valid.

If one passes in the value 0011111011, is that a valid value? Well, it depends on what you're looking for: if you are looking at the whole value, then it's an invalid value, but if you're looking at individual bits, it's an ok value: it has bits set which aren't defined, but that's ok, as flag-based enums are checked 'per bit': you're not comparing them to a value, you're checking whether a bit is set or not.

So, as your logic will check on which bits are set to select which formats to pick, it's realy not necessary to check whether the enum value is defined: you have 3 formats: if the bit of the corresponding format is set, the format is selected. That's the logic you should write.

like image 37
Frans Bouma Avatar answered Oct 26 '22 14:10

Frans Bouma