如何统一/简化此代码 - 事件处理/委托?

时间:2009-11-09 18:35:16

标签: c# .net refactoring delegates

我有从两个不同视图监视的Process对象。 Windows.Forms.ListView(实际上是派生类)和图形查看器(基于Microsoft Research的自动图形布局)。每个都有一个上下文菜单,可以激活类似的事件。虽然列表视图可以有多个选择,但我不允许在图表视图中使用它。

这就是我目前所拥有的:

    private void ctxgrphAddBreakpoint_Click(object sender, EventArgs e)
    {
        Process p = GetProcess(viewer);
        if (p != null)
        {
            p.AddBreakpoint();
            BeginRefresh(false, false);
        }
    }

    private void ctxgrphRemoveBreakpoint_Click(object sender, EventArgs e)
    {
        Process p = GetProcess(viewer);
        if (p != null)
        {
            p.RemoveBreakpoint();
            BeginRefresh(false, false);
        }
    }

    private void ctxlistAddBreakpoint_Click(object sender, EventArgs e)
    {
        foreach (Process p in lvwProcessList.SelectedProcesses())
        {
            p.AddBreakpoint();
        }
        BeginRefresh(false, false);
    }

    private void ctxlistRemoveBreakpoint_Click(object sender, EventArgs e)
    {
        foreach (Process p in lvwProcessList.SelectedProcesses())
        {
            p.RemoveBreakpoint();
        }
        BeginRefresh(false, false);
    }

我想将两个上下文菜单统一为一个,将事件处理统一为:

    private void ctxlistAction_Click(object sender, EventArgs e)
    {
        // I can unify the Viewer and ListView and implement some common interface,
        // so I'm pretty sure I can handle this part
        foreach (Process p in UIView.SelectedProcesses())
        {
            p.Action(); // What is the best way to handle this?
        }
        BeginRefresh(false, false);
    }

我如何到达那里?

4 个答案:

答案 0 :(得分:3)

访客模式?

答案 1 :(得分:2)

在* .Designer.cs文件中找到事件分配(... += new System.EventHandler(ctxlistAction_Click);),并使它们指向同一个函数。

答案 2 :(得分:0)

你不能把发送者强制转换成你的界面吗?

 private void ctxlistAction_Click(object sender, EventArgs e)
 {
      UIView view = sender as UIView;

      if (view != null)
      {

         // I can unify the Viewer and ListView and implement some common interface,
         // so I'm pretty sure I can handle this part
         foreach (Process p in view.SelectedProcesses())
         {
             p.Action(); // What is the best way to handle this?
         }
         BeginRefresh(false, false);
      }
  }

答案 3 :(得分:0)

首先,尝试重构常用功能并使用委托 (请原谅我选择不好的名字,比如“DoListAction”):

readonly Action<Process> removeBreakpoint = p => p.RemoveBreakpoint();
readonly Action<Process> addBreakpoint = p => p.AddBreakpoint(); 

void DoSingleAction(Action<Process> action)
{
     var p = GetProcess(viewer);
     if(p != null)
     {
         action(p); //invokes the action
         BeginRefresh(null,null);
     }
}

void DoListAction(Action<Process> action)
{
    lvwProcessList.ForEach(action);
    BeginRefresh(false, false);
}



private void ctxgrphAddBreakpoint_Click(object sender, EventArgs e)
{
    DoSingleAction(addBreakpoint);
}

private void ctxgrphRemoveBreakpoint_Click(object sender, EventArgs e)
{
    DoSingleAction(removeBreakpoint);
}

private void ctxlistAddBreakpoint_Click(object sender, EventArgs e)
{
    DoListAction(addBreakpoint);
}

private void ctxlistRemoveBreakpoint_Click(object sender, EventArgs e)
{
    DoListAction(removeBreakpoint);
}

然后你可以统一DoProcessAction和DoListAction:

void DoAction(object sender, Action<Process>)
{
     if(sender is ListView)
     {
         lvwProcessList.ForEach(action);
         BeginRefresh(false, false);
     }
     else if (sender is GraphView)
     {
         var p = GetProcess(viewer);
         if(p != null)
         {
             action(p); //invokes the action
             BeginRefresh(null,null);
         }
     }
     else {throw new Exception("sender is not ListView or GraphView");}
}

//and update all the handlers to use this, and delete DoSingleAction and DoListAction:
private void ctxgrphAddBreakpoint_Click(object sender, EventArgs e)
{
    DoAction(sender, addBreakpoint);
}
//etc.

无论如何,在每个事件处理程序中,我认为你必须指定要采取的行动。在这种情况下,我不确定继承或扩展方法是否真的会成为你的朋友,但是这里使用扩展方法可能看起来如何:

//these two are in a separate static class
public static InvokeAction(this ListView listView, Action<Process> action)
{ ... }
public static InvokeAction(this GraphView listView, Action<Process> action)
{ ... }

private void handler(object sender, Action<Process> action)
{ 
    var lv = sender as ListView;
    var gv = sender as GraphVeiw;    
    lv.InvokeAction(action); //(need to check for null first)
    gv.InvokeAction(action);
    if(lv == null && gv == null) {throw new Exception("sender is not ListView or GraphView");}
}

//then update all the handlers:
private void ctxgrphAddBreakpoint_Click(object sender, EventArgs e)
{
    handler(sender, addBreakpoint);
}


C#2 / .NET 2.0

要在C#2(VS2005)中执行此操作,使用委托的第一种方法仍然有效,你就是不能拥有lambdas。 (2.0有Action<T>代表)。只需将lambda更改为函数即可。其他一切都能正常运作。

void removeBreakpoint(Process p) { p.RemoveBreakpoint(); }
void addBreakpoint(Process p) { p.AddBreakpoint(); }

//you could also do this, but it's overkill:
readonly Action<Process> removeBreakpoint = delegate(Process p) { p.RemoveBreakpoint(); };
readonly Action<Process> addBreakpoint = delegate(Process p) { p.AddBreakpoint(); };