C# Generics won't allow Delegate Type Constraints
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
{
}
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
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
{
}
}
Solution 5
I came across a situation where I needed to deal with a Delegate
internally but I wanted a generic constraint. Specifically, I wanted to add an event handler using reflection, but I wanted to use a generic argument for the delegate. The code below does NOT work, since "Handler" is a type variable, and the compiler won't cast Handler
to Delegate
:
public void AddHandler<Handler>(Control c, string eventName, Handler d) {
c.GetType().GetEvent(eventName).AddEventHandler(c, (Delegate) d);
}
However, you can pass a function that does the conversion for you. convert
takes a Handler
argument and returns a Delegate
:
public void AddHandler<Handler>(Control c, string eventName,
Func<Delegate, Handler> convert, Handler d) {
c.GetType().GetEvent(eventName).AddEventHandler(c, convert(d));
}
Now the compiler is happy. Calling the method is easy. For example, attaching to the KeyPress
event on a Windows Forms control:
AddHandler<KeyEventHandler>(someControl,
"KeyPress",
(h) => (KeyEventHandler) h,
SomeControl_KeyPress);
where SomeControl_KeyPress
is the event target. The key is the converter lambda - it does no work, but it convinces the compiler you gave it a valid delegate.
(Begin 280Z28) @Justin: Why not use this?
public void AddHandler<Handler>(Control c, string eventName, Handler d) {
c.GetType().GetEvent(eventName).AddEventHandler(c, d as Delegate);
}
(End 280Z28)
Related videos on Youtube
Nicholas Mancuso
Assistant Professor at USC, Keck School of Medicine; Biostats @ Preventive Medicine and Center for Genetic Epi.
Updated on July 05, 2022Comments
-
Nicholas Mancuso almost 2 years
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?
-
Nicholas Mancuso over 15 yearsthats not really the functionality i'm looking for... I was trying to make a type constraint on my generic class...
-
Sam Harwell over 14 years@Justin: I edited your answer to put my comment at the end since it has a code block.
-
Sam Harwell over 14 years+1 for: 1) using the static constructor and 2) including a detailed message due to weird debugging conditions surrounding type initialization.
-
myermian over 11 years@MarcGravell: Doesn't throwing an exception in a static initializer violate
CA1065: Do not raise exceptions in unexpected locations
... I was always under the assumption that you should use a custom code analysis rule to find invalid usages of your class that aren't normally available at run-time. -
Justin Morgan over 11 yearsBroken link. Do you have a current one?
-
Jeppe Stig Nielsen almost 6 yearsYes, it is possible in C# 7.3 (since May 2018), and you can see release notes here.
-
Jeppe Stig Nielsen almost 6 yearsStarting in C# 7.3 (released May 2018), it is allowed to constrain like this,
where T : Delegate
, (and someone posted a new answer about that below). -
Elmue almost 4 yearsWhy do you repeat the question here? You don't tell us anything new.
-
Joel almost 3 yearsThis should be the new accepted answer, the current one is from 2008.. Very outdated now.