将委托/事件作为ref通用参数传递

时间:2018-09-25 21:21:14

标签: c# generics

我们正在处理大量WinForms旧代码,并且正在缓慢地进行重构,分解依赖关系,实现MVP模式并将逻辑从窗体和用户控件移到演示者中。

我们已经添加了Castle Windsor,它创建了一个ViewFactory,该ViewFactory返回带有Presenter及其附加所有依赖项的窗体/用户控件。

几乎每个屏幕上都使用一个用户控件,我们已经对其进行了重构,但尚未完成所有使用它的屏幕的重构,因此其中一些正在使用ViewFactory,而某些仍在调用该控件的构造函数,课程不会创建演示者。

我可以解决的方法是检查控件现在具有的事件是否为null,如果是,则意味着没有演示者已订阅它们,因此我手动创建它。这可以工作,但是由于有一些事件可以在不同的时间触发,这意味着我必须执行以下操作:

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())));
}

请注意,在创建演示者并订阅了视图的事件后,需要将该委托作为ref传递,以便成功调用该委托。

我不想对每种事件使用不同的重载方法,而是要这样做:

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

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

当然T1不包含 Invoke 方法的定义。到目前为止,我已经尝试过:

将动作投射到动作:

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

将T1重新定义为代表:

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

将T1重新定义为动作:

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

所有这些选项给我带来了不同的编译错误,所以我的问题是,这能完全实现吗?

1 个答案:

答案 0 :(得分:1)

这个问题很难解决,因为generics won't allow a delegate as a type constraint,所以我们不能编写一个通用方法来接受任何特定类型的委托。

但是我们可以在委托内的类型参数内指定类型约束。因此,解决方案是摆脱CustomDelegate1CustomDelegate2,而使用诸如CustomDelegate<T>之类的通用版本,并将事件arg类型作为通用参数传递。

这是委托和事件参数定义的外观:

public class CustomEventArgs1 : EventArgs { }


public class CustomEventArgs2 : EventArgs { }

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

这是您的方法:

public void RaiseEventForPresenter<T>(ref CustomDelegate<T> action, T args) where T : EventArgs
{
    if (action == null) CreatePresenter();
    action(this, args);
}