C# 2.0 introduced a great new feature to the .NET Type system: generics. Generics are really cool in that they allow you to define template classes; I can use a single class definition to provide a strongly typed collection, for example. They enable some other tricks that I would tend to consider something of a "hack" as well; for example, this expression evaluates to true:
1: typeof(IEnumerable<int>) != typeof(IEnumerable<double>)
This expression is nice because there are some odd class design decisions in places like the PropertyGrid's type structure. For example, in order to add a PropertyTab to the list of the PropertyGrid's tabs, you need to add a Type to the PropertyTabCollection exposed by the PropertyGrid's PropertyTabs property. The PropertyGrid caches the tabs that it creates, and so you can't add a single Type to create two property tabs. Consequently, even if you override the CreateTab method, you can't expect to add two tabs with the same Type.
My solution, then, was to create ExtensionPropertyTab<T>. This class's type parameter is utterly useless; I create an arbitrary Type using Reflection Emit, close my ExtensionPropertyTab generic type definition with it, and then add the PropertyTab with that closed type. Works great! This stuff will be in an upcoming blog post about my PropertyGridEx project.
All of that is leading up to my next hack and, ultimately, my wishlist item #1 for C# 4.0.
There's a simple design-time class called EnumConverter. This class is the default type converter for all enumeration types; EnumConverter is what displays the Enum names in the property grid when you're choosing items. I'm creating a type surrogate class that allows you to customize the names of properties, and I've also been working on displaying better values for enumerations. To this end, I created the EnumTypeConverter<T> class - this class provides enumeration names, but also retrieves friendly names from attributes on each enum entry. Using generics, I'm able to cache the friendly names so that reflection only needs to be invoked once; System.Enum does something similar.
What I'd like to do, however, is say this:
1: public class EnumTypeConverter<T> : TypeConverter where T : enum
C# doesn't allow me to do this. I get two errors:
error CS1031: Type expected
error CS1001: Identifier expected
So I try using the type name instead:
1: public class EnumTypeConverter<T> : TypeConverter where T : Enum
C# doesn't like this either:
error CS0702: Constraint cannot be special class 'System.Enum'
Why? I get the same error with System.ValueType, even though ultimately it means the same thing as "struct" (though I could understand this difference). But I can't do this with System.Delegate either (how about calling Invoke() or BeginInvoke() on T?).
Being able to specify Enum as a base would allow me to:
- Explicitly cast between T and integral numeric types.
- Specify 0 as the default value of T rather than using default(T).
- Perform bitwise operations on them
There's really no reason to have a constraint like "Constraint cannot be special class 'System.Enum'." Let's eliminate this artificial barrier - there shouldn't be any changes needed to be made to the CLR.