public enum TestEnum
{
Value1,
Value2,
Value3,
Value4
};
public void ShowEnumValues()
{
Enum.GetValues(typeof (TestEnum))
.Cast<TestEnum>()
.ToList()
.ForEach(e => Console.WriteLine(e));
}
With this, any changes to TestEnum automatically get picked up at run-time without any additional changes to the rest of the code.Last year, I wrote about using a dictionary to facilitate converting between an enum and human readable text. To adapt the code presented then to the above example, the following will convert all values in the enum to human readable text.
private IDictionary<TestEnum, string> enumToString =
new Dictionary<TestEnum, string>
{
{TestEnum.Value1, "Value one"},
{TestEnum.Value2, "Value two"},
{TestEnum.Value3, "Value three"},
{TestEnum.Value4, "Value four"}
};
public void ShowEnumStrings()
{
Enum.GetValues(typeof(TestEnum))
.Cast<TestEnum>()
.ToList()
.ForEach(e => Console.WriteLine(enumToString[e]));
}
In the previous article, I lamented the fact that this C# solution, unlike the Pascal variant, does not provide a reliable means to check that all the values of the enumeration have a corresponding entry in the dictionary. Because of strong typing with generics, I know I can't have any thing that's not part of the enumeration in the dictionary, but I can't know I have all the values of the enumeration in the dictionary. In other words, I know I can't have any invalid values, but I can't know I have all the values.
Until I found this method. Now I can at least write an easy test case for this. While it's not quite as good as a compile time check, it does give an early, reliable means of being alerted to the problem.
[TestMethod]
public void EnsureAllEnumValuesAreInDictionary()
{
Assert.IsTrue(
Enum.GetValues(typeof(TestEnum))
.Cast<TestEnum>()
.All(type => EnumToString.ContainsKey(type)),
"Enum value missing from enumToString dictionary");
}
Since variables of this type typically do not change from instance to instance, they can be made static so they're only instantiated once. And since many times they are only used inside a single class, they can be made private. In these cases having a unit test might be problematic, not being able to test a private field. To solve this, code similar to the above test can be put in a static constructor, throwing an exception if there's a mismatch.
private static readonly IDictionary<TestEnum, string> EnumToString =
new Dictionary<TestEnum, string>
{
{TestEnum.Value1, "Value one"},
{TestEnum.Value2, "Value two"},
{TestEnum.Value3, "Value three"},
{TestEnum.Value4, "Value four"}
};
static UnitTest1()
{
if (!Enum.GetValues(typeof (TestEnum))
.Cast<TestEnum>()
.All(type => EnumToString.ContainsKey(type)))
throw new InvalidOperationException(
"Enum value missing from enumToString dictionary");
}
Finally, if this needs to be done much, making a generic helper function will clean-up code, hiding all the casting and repetition of the type.
private static IEnumerable<T> GetEnumValues<T>()
{
return Enum.GetValues(typeof (T)).Cast<T>();
}
[TestMethod]
public void EnsureAllEnumValuesAreInDictionary1()
{
Assert.IsTrue(
GetEnumValues().All(type => EnumToString.ContainsKey(type)),
"Enum value missing from enumToString dictionary");
}
All in all, I'm pleased with finding this little nugget.
A complete VS2008 solution containing the code examples above is available on github.
Hope this helps someone else.
No comments:
Post a Comment
Comments are welcome but I do moderate them. This is simply to keep things wholesome for general family viewing. By default, comments will be accepted. The few things that will cause a comment to be rejected are:
1. It is too long even though it may be well-written and make interesting points. It's supposed to be a comment, not an essay. If you have that much to say, write a blog article and backlink to me.
2. It is nasty, impolite or uses language that is unacceptable.
3. It includes a a link that has a typo or is broken in some other way.
4. It should have been sent as an e-mail since it is clearly addressed to me and does not appear to have been intended for other readers.
5. It is blatantly self-promotional. This does not mean it can't be self-promotional at all, but it should add some value over an above the marketing.