locked
C# 4.0 Type Constraints: "where T : Enum" / "where T: Delegate" RRS feed

  • General discussion

  • Will C# 4.0 finally allow 'Delegate' and 'Enum' as constraints on type parameters?

    So far it is impossible to write generic methods (classes) with type constraits for these two. Both constraints actually produce valid IL and can also be consumed by C# if written in another language (you can declare those constraints in managed C++ or in IL).

    I'm perfectly aware that you cannot explicitly derive from either Delegate or Enum in C#, but any 'delegate' or 'enum' declaration does so implicitly :
    Open up the compiled code with IlDasm or Reflector, and any
    enum Foo [...]
    delegate Bar [...}

    definition will read
    .class public auto ansi sealed beforefieldinit Foo
        extends [mscorlib]System.Enum [...]
    .class public auto ansi sealed Bar
        extends [mscorlib]System.MulticastDelegate [...]

    And it actually makes sense when you want to write generic methods that can only work "on delegates" or "on enums".
    For example recently I tried to write a type safe method to get all values from an enum:

    public static T[] GetValues<T>() where T: Enum {
     return (T[]) Enum.GetValues(typeof(T));
    }
    

    Right now (in C# 3.0) I cannot express that constraint on the type parameter. But if I could the compiler would prevent me from calling that method for anything but an enumeration.

    At another time, while I was working with DynamicMethod class from System.Reflection.Emit, I actually would up implementing a ton of code in C++ just because C# wouldn't for the life of me, allow me to constrain a type parameter to a Delegate. While what I wanted to do (and could only do by moving a bunch of code to a seperate assembly written in managed C++) was

    public static TDelegate GetDelegate<TDelegate>(MethodInfo method) where TDelegate : Delegate { ... }
    MethodInfo info = ...
    Action act = GetDelegate<Action>(info);
    

    all I could have done in C# was:

    public static Delegate GetDelegate(MethodInfo method, Type delegateType) { ... }
    MethodInfo info = ...
    Action act = (Action)GetDelegate(info, typeof(Action));
    

    Again - C# due to it's inability to formulate that constraint would have resulted in clumsier, less precise code.
    Where the desired declaration with a Delegate type constraint would enforce at compile time that the type parameter for the method was a valid delegate type, the C# workaround does no such thing. It actually let's you pass typeof(decimal) as 'delegateType' parameter without complaint, and nothing prevents you (or more likely your sleep deprived coworker) from ultimately trying to cast the return value to a byte array. The only time you will notice this is at runtime, when your application blows up in your face :(
    Also the whole parameter "Type t" signature where you have to follow up with a cast to your desired return type is a constant reminder to the ugly, pre generic .Net 1.0 time.


    So, I'd really like to suggest relaxing the restriction on the CS0702 compile error:  "Constraint cannot be special class 'identifier'" for System.Enum & System.Delegate.

    As for System.ValueType - I've no problem with keeping that there. You can formulate that constraint with 'where T : struct'.
    Regarding not being able to declare 'where T : System.Array' I could make a point very similar to my remarks about Enum & Delegate. But since you cannot 'write' classes that derive from Array, I never checked IL output if arrays actually derive from that type or the runtime just pretends they do ;)
    Well - I just setup a demo class library in C++ with an Array, Delegate & Enum as type constraint on a generic method each, and C# correctly warns me, if I use that library and I try to pass in a parameter that is not of the appropriate type - so let's consider that a vote for Array as valid type constraint in C# as well.
    • Edited by TheDoctorMk2 Monday, April 6, 2009 2:55 AM formatting
    Monday, April 6, 2009 2:55 AM

All replies

  • I too would like to see more granular support for generic type constraints, particularly Enum and Delegate.  Currently, C++/CLI supports constraints on Enum--I'm not sure about Delegate.  I have some Enum helper methods currently defined in a C++/CLI project that I would love to migrate to C#:

    static array<T>^ GetValues()
    {
        return ((array<T>^) Enum::GetValues(T::typeid));
    }
    
    generic <typename T> where T: value class, Enum
    static Nullable<T> Parse(String^ value)
    {
        return EnumHelper::Parse<T>(value, false);
    }
    
    generic <typename T> where T: value class, Enum
    static Nullable<T> Parse(String^ value, bool ignoreCase)
    {
        if (String::IsNullOrEmpty(value))
            return Nullable<T>();
        return (T)Enum::Parse(T::typeid, value->Trim(), ignoreCase);
    }
    
    generic <typename T> where T: value class, Enum
    static bool TryParse(String^ value, [Out] T %result)
    {
        return TryParse(value, false, result);
    }
    
    generic <typename T> where T: value class, Enum
    static bool TryParse(String^ value, bool ignoreCase, [Out] T %result)
    {
        if (String::IsNullOrEmpty(value))
            false;
        Nullable<T> intermediateResult = Parse<T>(value, ignoreCase);
        result = intermediateResult.HasValue ? intermediateResult.Value : T();
        return intermediateResult.HasValue;
    }
    
    Monday, April 6, 2009 4:30 PM