0

I have this class where I am trying to create a template for one of my classes, and for that I needed to be able to pass a delegate type to define an event. I added the Delegate constraint to make sure it is a Delegate.

public abstract class TemplateScriptEvent<DelegateType> : ScriptEvent where DelegateType : Delegate
{
    public event DelegateType Event;
}

However, this causes

Error CS0066 '_ScriptEvent<DelegateType>.Event': event must be of a delegate type

Is what I am trying to do even possible, and why is this failing?

I am using the .Net Framework 4.7.2.

PixxlMan
  • 69
  • 1
  • 3
  • 11
  • Does this answer your question? [C# Generics won't allow Delegate Type Constraints](https://stackoverflow.com/questions/191940/c-sharp-generics-wont-allow-delegate-type-constraints) – Pavel Anikhouski Dec 06 '19 at 19:05
  • 1
    That answer is no longer appropriate - you can now use a delegate constraint. – György Kőszeg Dec 06 '19 at 19:28

1 Answers1

2

It does not work because you cannot enforce your generic argument to be a non-abstract derived type of MulticastDelegate, which would satisfy the C# event declaration (in other words, you can substitute the Delegate type itself in place of your constrained argument).

Recommended solution:

If you use conventional events, then you don't need to use the delegate constraint at all. Just use the generic EventHandler<T> delegate and substitute only its event argument:

public abstract class TemplateScriptEvent<TEventArgs> : ScriptEvent
    where TEventArgs : EventArgs
{
    public event EventHandler<TEventArgs> Event;

    protected virtual void OnEvent(TEventArgs e) => Event?.Invoke(this, e);
}

Abstract "event" for any delegate types:

If you really want to use any delegate type, then the closest solution you can do is something like this:

public abstract class TemplateScriptDelegate<TDelegate, TResult> : ScriptEvent
    where TDelegate : Delegate
{
    // this is a simple property rather than an event, but allows +=/-= for concrete delegate types
    // (and also simple assignments).
    public TDelegate Event { get; set; }

    // you must implement this in a more concrete derived type
    protected abstract TResult OnEvent();
}

// To be able to use this you need a derived type for each delegate types (which of course,
// can have type arguments for return value and parameters).
// For example, you can have a derived type for Func<T> delegates:
public class TemplateScriptFunc<TResult> : TemplateScriptDelegate<Func<TResult>, TResult>
{
    protected override TResult OnEvent() => Event.Invoke();
}
György Kőszeg
  • 17,093
  • 6
  • 37
  • 65
  • I used your recommended solution, it works well, thank you! Is it possible to make an event args that does not require any parameters in the receiving object, or do I need to use custom delegates for that? – PixxlMan Dec 06 '19 at 23:02
  • 1
    If you don't need any event arguments you can use the non-generic `EventHandler` delegate or the base `EventArgs` class and the `EventArgs.Empty` singleton instance. – György Kőszeg Dec 07 '19 at 11:25