代码之家  ›  专栏  ›  技术社区  ›  Cade Roux

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

  •  0
  • Cade Roux  · 技术社区  · 15 年前

    我有从两个不同视图监视的流程对象。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 回复  |  直到 15 年前
        1
  •  3
  •   Kelly S. French    15 年前

    访客模式?

        2
  •  2
  •   Nestor    15 年前

    定位事件分配( ... += new System.EventHandler(ctxlistAction_Click); )在*.designer.cs文件中,使它们指向相同的函数。

        3
  •  0
  •   mdm20    15 年前

    你不能把发送者强制转换到你的接口上吗?

     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);
          }
      }
    
        4
  •  0
  •   dan    15 年前

    首先,尝试重构公共功能并使用委托 (请原谅我对“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和dolisaction:

    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

    要在C 2(VS2005)中实现这一点,使用委托的第一种方法仍然有效,您不能使用lambda。(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(); };