Duh moment: in C#, the integer zero (0) is compatible with all enums
Posted by jpluimers on 2011/11/30
Even if you have used something for over a decade, you can learn about it :)
I was refactoring bits of code where someone clearly didn’t understand the benefits of enumerations, similar to this very contrived example:
using System;
namespace Demo
{
// explicit equivalence of:
// public enum TrafficLight { Red, Yellow, Green };
public enum TrafficLight { Red = 0, Yellow = 1, Green = 2 };
public class Program
{
public static void Main()
{
// old code:
Console.WriteLine(IntIsRedTrafficLight((int)TrafficLight.Red));
Console.WriteLine(IntIsRedTrafficLight((int)TrafficLight.Yellow));
Console.WriteLine(IntIsRedTrafficLight((int)TrafficLight.Green));
}
public static bool IntIsRedTrafficLight(int trafficLight)
{
return (trafficLight == (int)TrafficLight.Red);
}
}
}
The code was using way too many casts, and my goal was something as simple as this:
using System;
namespace Demo
{
public enum TrafficLight { Red, Yellow, Green };
public class Program
{
public static void Main()
{
Console.WriteLine(IsRed(TrafficLight.Red));
Console.WriteLine(IsRed(TrafficLight.Yellow));
Console.WriteLine(IsRed(TrafficLight.Green));
}
public static bool IsRed(TrafficLight trafficLight)
{
return (trafficLight == TrafficLight.Red);
}
}
}
Often when refactoring code into something that can be better enforced by the compiler, I start changing the central code, then have the compiler mark all the errors.
Well, in this case, I got less compiler errors than expected:
using System;
namespace Demo
{
public enum TrafficLight { Red = 0, Yellow = 1, Green = 2 };
public class Program
{
public static void Main()
{
// simplified code: about to remove the (int) casts
Console.WriteLine(IsRed((int)TrafficLight.Red)); // no compiler errror!
Console.WriteLine(IsRed((int)TrafficLight.Yellow)); // compiler error: Argument 1: cannot convert from 'int' to 'Demo.TrafficLight'
Console.WriteLine(IsRed((int)TrafficLight.Green)); // compiler error: Argument 1: cannot convert from 'int' to 'Demo.TrafficLight'
}
public static bool IsRed(TrafficLight trafficLight)
{
return (trafficLight == TrafficLight.Red);
}
}
}
What happens is that the int value 0 is compatible with all enumeration types that have an enumeration value mapping to zero.
I had a duh moment here, as I didn’t expect that. But reading the ECMA C# standard section on enums, it makes sense.
The trick to force the compiler to generate errors is to make sure none of the elements map to zero:
using System;
namespace Demo
{
// public enum TrafficLight { Red = 0, Yellow = 1, Green = 2 };
// trick to make all assignments fail: make Read != 0
public enum TrafficLight { Red = 3, Yellow = 1, Green = 2 };
public class Program
{
public static void Main()
{
// simplified code: about to remove the (int) casts
Console.WriteLine(IsRed((int)TrafficLight.Red)); // compiler error: Argument 1: cannot convert from 'int' to 'Demo.TrafficLight'
Console.WriteLine(IsRed((int)TrafficLight.Yellow)); // compiler error: Argument 1: cannot convert from 'int' to 'Demo.TrafficLight'
Console.WriteLine(IsRed((int)TrafficLight.Green)); // compiler error: Argument 1: cannot convert from 'int' to 'Demo.TrafficLight'
}
public static bool IsRed(TrafficLight trafficLight)
{
return (trafficLight == TrafficLight.Red);
}
}
}
–jeroen






Leave a comment