It looks like you need an interface type, not a delegate. Interface methods can accept open generic types (this is what you need), although delegates cannot. For example, you could define something like:
interface ActOnConstrainedThing<CT1,CT2> { void Act<MainType>(MainType param) where MainType: CT1,CT2; }
Even if the implementers CT1 and CT2 do not have a common base type, which also implements CT1 and CT2 , the Act implementation can use its passed parameter as CT1 or CT2 without matching the type and can even pass it to routines that expect a common parameter with CT1 restrictions and CT2 . This may not be possible with delegates.
Note that using interfaces rather than delegates means that you cannot use the usual event mechanism and syntax. Instead, the object that will be the event publisher must maintain a list of object instances that implement the required interface (for example, a List<ActOnConstrainedThing<IThis,IThat>> ), and list instances in this list (possibly using foreeach ). For example:
List <IActOnConstrainedThing <IThis, IThat >> _ActOnThingSubscribers;
void ActOnThings <T> (T param) where T: IThis, IThat
{
foreach (var thing in _ActOnThingSubscribers)
{
thing.Act <T> (param);
}
}
Edit / Add
In the place where I used this template, there were also some other things that did not seem overly relevant for the question, which in my interpretation asked the question of how to have a delegate (or equivalent) with an open parameter, an object that calls the delegate equivalent , can provide a type parameter if the object did not provide the delegate in order to know it in advance. Most of the cases where this is useful are related to general limitations, but since this seems to introduce confusion, here is an example that does not:
interface IShuffleFiveThings
{
void Shuffle <T> (ref T p1, ref T p2, ref T p3, ref T p4, ref T p5);
}
List <IShuffleFiveThings _ShuffleSubscribers;
void ApplyShuffles <T> (ref T p1, ref T p2, ref T p3, ref T p4, ref T p5)
{
foreach (var shuffler in _ShuffleSubscribers)
{
thing.Shuffle (ref p1, ref p2, ref p3, ref p4, ref p5);
}
}
The IShuffleFiveThings.Shuffle<T> method takes five parameters with ref and does something with them (most likely, they rebuild them in some way, possibly rearranging them randomly or possibly rearranging some randomly, leaving others where they are). list IShuffleFiveThings , then this list can be effectively used without boxing or reflection to manipulate any things (including class types and value types). In contrast, if someone used delegates:
delegate void ActOn5RefParameters (ref p1, ref p2, ref p3, ref p4, ref p5);
because any particular delegation instance can act on only one type of parameter provided when it is created (unless it is an open delegate that is called only through Reflection), it would be necessary to create a separate list of delegates for each type of object that needed to be shuffled (yes, I know that you can usually handle permutations using an array of integer indices, I chose the permutation as an operation, because it applies to all types of objects, and not because this con the specific permutation method is all useful).
Please note that since the type T in IShuffleFiveThings does not have any restrictions, the implementation will not be able to do much with it except using type casting (which can lead to boxing). Adding constraints to such parameters makes them much more useful. Although it would be possible to hard-code such restrictions in an interface, which would limit the usefulness of the interface to applications that require these specific restrictions. Creating general restrictions restricts this restriction.