Tuesday, July 28, 2009

Constraint cannot be special class 'System.Enum'

My esteemed colleague and all-round nice guy Dan Esparza came up with a great suggestion for an improvement to my ASP.NET MVC DropDownList from Enum post from yesterday. His idea, which I was very excited about, was:

Couldn't you use syntax similar to:
public static IDictionary<int, string> EnumToDictionary(this Type type) where T: Enum
Then, your extension method should only appear on Enum's

To test out anything like this I love to do red/green unit testing. i.e. demonstrate that it fails first, fix it and then demonstrate that it passes next. The pattern here is slightly different inasmuch as I wanted to demonstrate that the function compiles and runs as previously designed and then change the design and see the compile fail and not the execution of the unit test - i.e. the unit test should not even be able to compile this time - that would be the success of the test.

In the previous post I had negligently forgotten to add a unit test to test for the exception being thrown in the extension method that I wrote. So I have added one:

[TestMethod]
[ExpectedException(typeof(InvalidCastException), "Attempted to use an invalid type.")]
public void test_enum_to_dictionary_exception()
{
    IDictionary<int, string> actual = typeof(Int32).EnumToDictionary();
}

I ran the unit test and it passed demonstrating that the original functionality threw an exception (and the right type of exception) when called with an invalid type. So far so good.

Next I went back to the extension method and changed its signature to read:

public static IDictionary<int, string> EnumToDictionary<T>(this T type) where T : Enum

...and compiled the project only to be rudely told: Constraint cannot be special class 'System.Enum'

More investigation shows the C# 2.0 specification to have the following comments on constraints:

A class-type constraint must satisfy the following rules:

  • The type must be a class type.
  • The type must not be sealed.
  • The type must not be one of the following types: System.Array, System.Delegate, System.Enum, or System.ValueType.
  • The type must not be object. Because all types derive from object, such a constraint would have no effect if it were permitted.
  • At most one constraint for a given type parameter can be a class type.

So that brought that idea to a screeching halt. Microsoft provide a workaround to this limitation by suggesting the use of a struct in place of the enum to increase the compile-time failures when using inappropriate types. However I could not get this to compile using my extension method pattern. This could just be my personal limitation and lack of depth in understanding generics and C# and a superior programmer may way be able to demonstrate the application of this workaround this to me...

2 comments:

  1. Hi :D
    I can't seem to find any information about your branded rocks. Please assist :P

    ReplyDelete
  2. link to the microsoft's work around is broken! :(

    ReplyDelete