1

We are working on a big piece of WinForms legacy code and are in the slow process of refactoring, breaking dependencies down, implementing the MVP pattern and moving logic out of Forms and User Controls into presenters.

We have already added Castle Windsor which creates a ViewFactory that returns a Form/User Control with a Presenter and all of its dependencies attached to it.

One user control is used in pretty much every single screen and we are already refactoring it but have not completed refactoring all the screens that use it, so some of them are using the ViewFactory and some are still calling the control's constructor which of course does not create a presenter.

The way I though we could work around that was to check if the events the control now has are null, if they are it means there is no presenter that has subscribed to them so I create it manually. This works but since there are a few events that can be fired at different times it means that I had to do the following:

private void RaiseEventForPresenter(ref EventHandler action, EventArgs e)
{
   if(action == null) CreatePresenter();

   action.Invoke(this, e);
}

private void RaiseEventForPresenter(ref CustomDelegate1 action, CustomDelegate1EventArgs e)
{
   if(action == null) CreatePresenter();

   action.Invoke(this, e);
}

private void RaiseEventForPresenter(ref CustomDelegate2 action, CustomeDelegate2EventArgs e)
{
   if(action == null) CreatePresenter();

   action.cInvoke(this, e);
}

private void CreatePresenter()
{
    new Presenter(this, new Bl(new Dal(new ConnectionManager())));
}

Notice that the delegate needs to be passed as ref in order for it to be successfully invoked after the presenter has been created and it has subscribed to the view's events.

Instead of having a different overloaded method for each kind of event, I would like to do something like so:

private void RaiseEventForPresenter<T1, T2>(ref T1 action, T2 e)
{
    if (action == null) CreatePresenter();

    action.Invoke(this, e); //Does not compile
}

Of course T1 does not contain a definition for the Invoke method. So far I have tried:

Casting action to an Action:

((Action<object, T2)action).Invoke(this, e);

Redefining T1 to a Delegate:

private void RaiseEventForPresenter<T>(ref Delegate action, T e) ...

Redefining T1 to an Action:

private void RaiseEventForPresenter<T>(ref Action<object, T> action, T e) ...

All of these options give me different compilation errors so my question is, can this be accomplished at all?

Sergio Romero
  • 6,477
  • 11
  • 41
  • 71
  • Why are you creating tons of different delegates that have the same signature? Just use the same delegate, preferably `Action`/`Func`, rather than constantly declaring new equivalent delegates. – Servy Sep 25 '18 at 21:25
  • How about `private void RaiseEventForPresenter( ref EventHandler action, T e )`? Not sure how this would be different then your last example though. Can you post the errors you get for your alternatives? – clcto Sep 25 '18 at 21:31
  • Not related to the main question, but why don't you make your `Presenter` object as a `Singleton`? you can even register it using Winsor – Javier Capello Sep 25 '18 at 21:50
  • @JavierCapello Given that they're creating multiple presenters, what gives you the impression that there should only ever be exactly one presenter? – Servy Sep 25 '18 at 21:52
  • @Servy I see, why do you need to pass the handler as a reference? isn't a delegate a reference type variable already? – Javier Capello Sep 25 '18 at 21:58
  • 1
    @JavierCapello For the same reason you'd pass any variable as a reference. Because you want to mutate the variable, and not just access the value in it. The fact that it's a reference type doesn't change that at all. – Servy Sep 25 '18 at 22:06

1 Answers1

1

This problem is tricky to solve because generics won't allow a delegate as a type constraint, so we can't write a generic method that will accept any particular type of delegate.

But we can specify a type constraint on a type parameter within a delegate. So the solution is to get rid of CustomDelegate1 and CustomDelegate2 and instead use a generic version such as CustomDelegate<T> and pass the event arg type as the generic parameter.

Here's how the delegate and event argument definitions might look:

public class CustomEventArgs1 : EventArgs { }


public class CustomEventArgs2 : EventArgs { }

public delegate void CustomDelegate<T>(object sender, T e) where T : EventArgs;

And here is your method:

public void RaiseEventForPresenter<T>(ref CustomDelegate<T> action, T args) where T : EventArgs
{
    if (action == null) CreatePresenter();
    action(this, args);
}
John Wu
  • 50,556
  • 8
  • 44
  • 80
  • The problem with this solution is how can it be used since the idea is to remove all the specific methods and leave only the generic one and when I try calling the generic method like this: RaiseEventForPresenter(ref CustomDelegate2, new CustomDelegate2EventArgs()); it does not compile with the message: "Cannot convert ref CustomDelegate2 to Action" – Sergio Romero Sep 26 '18 at 14:54