5

I'm trying to load some AppSettings into an object. The settings look like this:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="Logging_default_path" value="C:\Temp" />
    <add key="Logging_max_file_size" value="10485760" />
    <add key="Logging_levels" value="Error,Warning,Info"/>
    <add key="Logging_filename" value="RobinsonLog" />
  </appSettings>
</configuration>

The Logging_levels represents several enum values that are allowed by the settings. I'm trying to load these into my object by using the following code:

Level = (LogLevel)Enum.Parse(typeof(LogLevel), settings["Logging_levels"]);

But this is not working and I'm only getting LogLevel.Info returned, not the value of Loglevel.Error | LogLevel.Warning | LogLevel.Info. The enum is defined as followed:

[Flags]
public enum LogLevel
{
    Error = 0x0,
    Warning = 0x1,
    Info = 0x2,
    Debug = 0x3,
    Performance = 0x4
}

Am I wrong by defining the values in hex? or did I miss something else?

codingbunny
  • 3,989
  • 6
  • 29
  • 54
  • Have you tried creating an enum with your expected value and converting this to string? This should show the proper syntax. – jv42 Nov 23 '11 at 13:26
  • 2
    Your flags values are wrong. The way it's written `Warning | Info == Debug` and `Debug == Debug | Error`. – svick Nov 23 '11 at 13:28

4 Answers4

4

Proper flag values might help (each value should set a different bit):

[Flags]
public enum LogLevel
{
    None = 0x0,
    Error = 0x1,
    Warning = 0x2,
    Info = 0x4,
    Debug = 0x8,
    Performance = 0x10
}

Note: you can remove the 'None' if you wish.

jv42
  • 8,521
  • 5
  • 40
  • 64
4

Your enum values are going to cause problems.

The values of a Flags enum must be powers of two, and you shouldn't use 0 for any value other than some kind of empty/none/nothing indicator.

Does this make any difference?

[Flags]
public enum LogLevel
{
    None        =  0
    Error       =  1,
    Warning     =  2,
    Info        =  4,
    Debug       =  8,
    Performance = 16
}
LukeH
  • 263,068
  • 57
  • 365
  • 409
  • This did the trick. Seems the hex values are indeed not appreciated by the parser. – codingbunny Nov 23 '11 at 13:36
  • 1
    Technically, they don't *have to be* powers of two. For example, `[Flags] enum FooBar { Foo = 5, Bar = 10 }` would work correctly too. But doing that would be completely unnecessary complication. There is no reason not to use powers of two. – svick Nov 23 '11 at 13:37
  • 1
    @NekoNova, it's not about using hex or decimal. It's about specifying the correct values. – svick Nov 23 '11 at 13:38
  • @NekoNova: It's nothing to do with whether or not the value literals are written in hex. The values must be *powers of two*. It will work exacly the same if you declare them as `0x00`, `0x01`, `0x02`, `0x04`, `0x08`, and `0x10`. – LukeH Nov 23 '11 at 13:39
3

http://msdn.microsoft.com/en-us/library/essfb559.aspx

The value parameter contains the string representation of an enumeration member's underlying value or named constant, or a list of named constants delimited by commas (,). One or more blank spaces can precede or follow each value, name, or comma in value. If value is a list, the return value is the value of the specified names combined with a bitwise OR operation.

So it should work, and ik looks like it does:

LogLevel level = (LogLevel)Enum.Parse(typeof(LogLevel), "Error,Warning");

if ((level & LogLevel.Error) == LogLevel.Error)
{
    Console.WriteLine("Error");
}

if ((level & LogLevel.Warning) == LogLevel.Warning)
{
    Console.WriteLine("Warning");
}

if ((level & LogLevel.Info) == LogLevel.Info)
{
    Console.WriteLine("Info");
}

Gives me "Error" and "Warning". However, by inspecting the level variable in Visual Studio, it only shows "Warning". Perhaps that set you off. ;-)


Edit: @svick and @jv42 are right, it are your flag values that are wrong.

Community
  • 1
  • 1
CodeCaster
  • 147,647
  • 23
  • 218
  • 272
2

The problem isn't the parsing! The problem is the conversion to text, instead. Because the flag values aren't distinct bit masks, there is no deterministic mapping to string and back. As mentioned by others, you should choose the values like e.g.:

[Flags]
public enum LogLevel
{
    None = 0
    Error = 1 << 0,
    Warning = 1 << 1,
    Info = 1 << 2,
    // etc
}

See MSDN: Parsing Enumeration Values

See a demo live: https://ideone.com/8AkSQ

using System;

[Flags] enum Colors { None=0, Red = 1, Green = 2, Blue = 4 };

public class Example
{
   public static void Main()
   {
      string[] colorStrings = { "0", "2", "8", "blue", "Blue", "Yellow", "Red, Green" };
      foreach (string colorString in colorStrings)
      {
         try {
            Colors colorValue = (Colors) Enum.Parse(typeof(Colors), colorString);        
            if (Enum.IsDefined(typeof(Colors), colorValue) | colorValue.ToString().Contains(","))  
               Console.WriteLine("Converted '{0}' to {1}.", colorString, colorValue.ToString());
            else
               Console.WriteLine("{0} is not an underlying value of the Colors enumeration.", colorString);
         }
         catch (ArgumentException) {
            Console.WriteLine("'{0}' is not a member of the Colors enumeration.", colorString);
         }
      }
   }
}

Output:

Converted '0' to None.
Converted '2' to Green.
8 is not an underlying value of the Colors enumeration.
'blue' is not a member of the Colors enumeration.
Converted 'Blue' to Blue.
'Yellow' is not a member of the Colors enumeration.
Converted 'Red, Green' to Red, Green.

Performance Note:

If performance is important, do not rely on Enum.Parse :)

See How should I convert a string to an enum in C#?

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • According to [the doc](http://msdn.microsoft.com/en-us/library/essfb559%28v=VS.80%29.aspx), parsing flags is supported at least since .Net 2.0. – svick Nov 23 '11 at 13:30
  • Read that article, and I have the flags attribute, but it does not explain how to parse a string into a collection of flags – codingbunny Nov 23 '11 at 13:31
  • The sample says: `Converted 'Red, Green' to Red, Green`. Do you want to get a collection of flags from the combined value? That has _zero_ to do with parsing – sehe Nov 23 '11 at 13:36
  • your code does not perform what I intended. I can parse single values without problems, its the combination of values that cuases the problem – codingbunny Nov 23 '11 at 13:36
  • @NekoNova: ah, the flags values should be non-overlapping to do what you expect too, as mentioned by other, notably svick! – sehe Nov 23 '11 at 13:37
  • yes, but I thought those hex values would have been fine, but seems it wasn't.... :S Sometimes I make it so complex for myself... – codingbunny Nov 23 '11 at 13:38