Вопрос

I have an XML that contains several flags, some of them are unsigned 32-bit integers and others are unsigned 64-bit integers. Some of them are written in a comma-separated list and others are in hex style.

See this example:

<Color>Blue,Red</Color>
<Color>0xC</Color>

As I don't want to write a method to parse each enum, I decided to use a generic method. But Visual Studio won't let me build the solution. Here's my method:

public static T ParseFlags<T>(string value) where T : struct
{
    T result = (T)((object)0);
    string[] array;
    // Remove white spaces and delimit string if it is comma-separated
    if (ParseDelimitedString(value, ',', out array))
    {
        for (int i = 0; i < array.Length; i++)
        {
            T flag = (T)((object)0);
            // Check if value is member of enumeration
            if (Enum.TryParse<T>(array[i], out flag))
            {
                result |= (T)((object)flag);
            }
        }
    }
    else
    {
        switch (Type.GetTypeCode(Enum.GetUnderlyingType(typeof(T))))
        {
            // Remove hex characters and parse node's inner text
            case TypeCode.UInt32:
                result = (T)((object)ParseUint(value));
                break;
            case TypeCode.UInt64:
                result = (T)((object)ParseUlong(value));
                break;
        }
    }
    return result;
}

The error message I get is:

Error 1 Operator '|=' cannot be applied to operands of type 'T' and 'T'

Is there a way to do this?

Это было полезно?

Решение

You are doing a lot of work that can be done for you. For example, if your enum is declared with the FlagsAttribute then Enum.Parse will parse comma separated values for you.

public static T ParseFlags<T>(string value) where T : struct
{
    T result;
    ulong temp;
    if (Enum.TryParse(value, out result))
    {
        return result;
    }

    string hexNum = value.StartsWith("0x") ? value.Substring(2) : value;
    if (ulong.TryParse(hexNum, NumberStyles.HexNumber, null, out temp))
    {
        return (T)Enum.ToObject(typeof(T), temp);
    }

    throw new ArgumentException("value could not be parsed");
}

I tested this with various Flags enum types with short, int, and ulong backing values.

Другие советы

If you know what type of enum you're parsing:

    [Fact]
    public void when_parsing_options_then_can_combine_flags()
    {
        var values = "Singleline | Compiled";

        var options = values.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
            .Select(value => (RegexOptions)Enum.Parse(typeof(RegexOptions), value))
            .Aggregate(RegexOptions.None, (current, value) => current |= value);

        Assert.Equal(RegexOptions.Singleline | RegexOptions.Compiled, options);
    }

Try this:

public static T ParseFlags<T>(string value) where T : struct
{
    long result = 0L;
    string[] array;
    // Remove white spaces and delimit string if it is comma-separated
    if (ParseDelimitedString(value, ',', out array))
    {
        for (int i = 0; i < array.Length; i++)
        {
            T flag = default(T);
            // Check if value is member of enumeration
            if (Enum.TryParse<T>(array[i], out flag))
            {
                result |= (long)flag;
            }
        }
    }
    else
    {
        switch (Type.GetTypeCode(Enum.GetUnderlyingType(typeof(T))))
        {
            // Remove hex characters and parse node's inner text
            case TypeCode.UInt32:
                result = ParseUint(value);
                break;
            case TypeCode.UInt64:
                result = ParseUlong(value);
                break;
        }
    }
    return (T)((object)result);
}

Hope it helps.

"|=" usage in that snippet makes me think you meant to use the Enum as bitset not just any old Enum. If this is true, you should make a small change - declare the local "result" as int and adjust the casts appropriately, your return statement should then be "return (T)(object)result;". That line in question with "|" would look like: "result |= (int)(object)flag;". Perhaps, there is a better answer but, note that enums are integers and your scenario of bitset are covered well enough with this solution unless there are situations I missed or you did not state.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top