Today, I figured out it might be interesting to have some sort of enumeration in C# similar to the class enums in Java (which has been done numerous times) but as immutable structs. I came up with something like this:
public struct TestStruct {
public static readonly TestStruct Value1 = 1;
public static readonly TestStruct Value2 = 2;
public static readonly TestStruct Value3 = 3;
private int _value;
private TestStruct(int value) {
_value = value;
}
public static implicit operator int(TestStruct instance) {
return instance._value;
}
}
The code compiles fine, however when I try to access TestStruct.Value2
, a NullReferenceException
occurs.
This was just a test, but now I'm wondering.. what happens to the static readonly TestStructs
? Why can I assign an integer to them, although they are not being initialized?
Update
This one compiles.
public struct LogLevel
{
// log levels
public static readonly LogLevel Information = 0x0000;
public static readonly LogLevel Warning = 0x0001;
public static readonly LogLevel Error = 0x0002;
public static readonly LogLevel Verbose = 0x0004;
public static readonly LogLevel Debug = 0x0008;
public static readonly LogLevel Trace = 0x0016;
public static readonly LogLevel Critical = 0x0032;
public static readonly LogLevel Fatal = 0x0064;
private static readonly Dictionary<int, LogLevel> Levels
= new Dictionary<int, LogLevel>();
private readonly int _value;
private LogLevel(int value)
{
if (Levels.ContainsKey(value))
throw new ArgumentException("Level already defined.");
_value = value;
Levels.Add(value, this);
}
public static implicit operator LogLevel(int value)
{
LogLevel level;
if (!Levels.TryGetValue(value, out level))
throw new ArgumentOutOfRangeException("value");
return level;
}
public static implicit operator int(LogLevel level)
{
return level._value;
}
}
Update 2
// extension method for log levels
public static void Debug(this ILogger logger, string message)
{
logger.Write(message, LogLevel.Debug);
}
// method on ILogger, implementation is Log4NetLogger (wrapper)
void Write(string message, LogLevel severity);
// call (simple)
Logger.Debug("Test");
There are two problems here:
You're using the implicit conversion from int
when you're initializing the fields... that expects the value to already be there. You should be using the constructor instead. So this:
public static readonly LogLevel Information = 0x0000;
should be
public static readonly LogLevel Information = new LogLevel(0x0000);
You're trying to access the dictionary before the Levels
variable has been initialized. You need to move this statement:
private static readonly Dictionary<int, LogLevel> Levels
= new Dictionary<int, LogLevel>();
... above the field declarations. That way it's initialized before the field initializers call the constructor.
At that point, it should work. It's not necessarily a design I'd use myself, but it at least removes the issues you've got at the moment.
See more on this question at Stackoverflow