C# Generics won't allow Delegate Type Constraints

Is it possible to define a class in C# such that

class GenericCollection<T> : SomeBaseCollection<T> where T : Delegate

I couldn't for the life of me accomplish this last night in .NET 3.5. I tried using

delegate, Delegate, Action<T> and Func<T, T>

It seems to me that this should be allowable in some way. I'm trying to implement my own EventQueue.

I ended up just doing this [primitive approximation mind you].

internal delegate void DWork();

class EventQueue {
    private Queue<DWork> eventq;
}

But then I lose the ability to reuse the same definition for different types of functions.

Thoughts?


Solution 1:

A number of classes are unavailable as generic contraints - Enum being another.

For delegates, the closest you can get is ": class", perhaps using reflection to check (for example, in the static constructor) that the T is a delegate:

static GenericCollection()
{
    if (!typeof(T).IsSubclassOf(typeof(Delegate)))
    {
        throw new InvalidOperationException(typeof(T).Name + " is not a delegate type");
    }
}

Solution 2:

Yes it's possible in C# 7.3, Constraints family increased to include Enum, Delegate and unmanaged types. You can write this code without a problem:

void M<D, E, T>(D d, E e, T* t) where D : Delegate where E : Enum where T : unmanaged
    {

    }

From Docs:

Beginning with C# 7.3, you can use the unmanaged constraint to specify that the type parameter must be a non-nullable unmanaged type. The unmanaged constraint enables you to write reusable routines to work with types that can be manipulated as blocks of memory

Useful links:

The future of C#, from Microsoft Build 2018

What's new in C# 7.3?

Solution 3:

Edit: Some proposed work-arounds are proposed in these articles:

http://jacobcarpenters.blogspot.com/2006/06/c-30-and-delegate-conversion.html

http://jacobcarpenters.blogspot.com/2006_11_01_archive.html


From the C# 2.0 specification we can read (20.7, 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.

And sure enough VS2008 spits out an error:

error CS0702: Constraint cannot be special class 'System.Delegate'

For info and investigation on this issue read here.

Solution 4:

If you are willing to take a compile time dependency on an IL Weaver you can do this with Fody.

Using this addin to Fody https://github.com/Fody/ExtraConstraints

Your code can look like this

public class Sample
{
    public void MethodWithDelegateConstraint<[DelegateConstraint] T> ()
    {        
    }
    public void MethodWithEnumConstraint<[EnumConstraint] T>()
    {
    }
} 

And be compiled to this

public class Sample
{
    public void MethodWithDelegateConstraint<T>() where T: Delegate
    {
    }

    public void MethodWithEnumConstraint<T>() where T: struct, Enum
    {
    }
}