如何防止重复,宏或什么?

时间:2010-05-08 13:07:11

标签: c# delegates

嗯,问题是我为每个传递给GUI的事件都有很多这样的代码,我该如何缩短呢?我想,宏不会做这项工作。是否有更通用的方法来做“模板”这样的事情?

private delegate void DownloadProgressDelegate(object sender, DownloaderProgressArgs e);
void DownloadProgress(object sender, DownloaderProgressArgs e) {
    if (this.InvokeRequired) {
        this.BeginInvoke(new DownloadProgressDelegate(DownloadProgress), new object[] { sender, e });
        return;
    }

    label2.Text = d.speedOutput.ToString();
}

编辑:

好的,我怎么能在BeginInvoke中使用匿名委托来实现这个目的:

this.BeginInvoke(new DownloadProgressDelegate(DownloadProgress), new object[] { sender, e });

6 个答案:

答案 0 :(得分:1)

我个人更喜欢将实际操作放在Action中,然后检查this是否需要调用 - 这样做的好处是不需要声明这么多delegate个与BeginInvoke一起使用。换句话说,

void DownloadProgress(object sender, DownloaderProgressArgs e) 
{
    Action updateLabel = () => label2.Text = d.speedOutput.ToString();

    if (this.InvokeRequired) 
    {
        this.BeginInvoke(updateLabel);
    }
    else
    {
       updateLabel();
    }
}

void DownloadSpeed(object sender, DownloaderProgressArgs e) {

    Action updateSpeed = () => 
    {
        string speed = "";
        speed = (e.DownloadSpeed / 1024).ToString() + "kb/s";
        label3.Text = speed;
    };

    if (this.InvokeRequired)
    {
        this.BeginInvoke(updateSpeed);
    }
    else
    {
        updateSpeed();
    }
}

这种方法非常适合在Control上使用Action的扩展方法,并通过InvokeRequired检查来运行它。

至少,扩展方法应该类似于:

public static void MaybeInvoke(this Control c, Action action)
{
   if (c.InvokeRequired)
   {
       this.BeginInvoke(action);
   }
   else
   {
       action();
   }
}

令人讨厌的是,在.NET 3.5之前没有引入非泛型Action,所以你需要在我给出的示例中稍微修改一下 - 可能使用MethodInvoker - 如果你我正在使用早期版本。

答案 1 :(得分:0)

你可以试试T4 ...但我不知道它是否适合你的情况。

答案 2 :(得分:0)

一个简单的想法是创建一个例程,根据请求类型枚举和使用switch语句处理所有BeginInvoke案例。那么至少你只需要检查一次InvokeRequired。请注意,您应该使用if / else而不是return来控制流程。

答案 3 :(得分:0)

嗯,一种方法可能是将通用代码放在静态类中,并像访问它一样访问它:

Utility.DownloadSpeedUpdate(frm, sender, e);

答案 4 :(得分:0)

.net(在某种意义上)是一个相当差的UI框架,它积极地鼓励你所遇到的问题,以及业务逻辑和UI的混合。

您可以使用泛型,委托和基类/静态助手方法取得一些进展。

但是,理想情况下,您需要在.net之上层叠UI管理器,以便正确地帮助您解决此问题。这将允许您将UI与其执行的命令分开(即,您可以动态地将UI事件(如按键,菜单选项,按钮单击等)绑定到基础命令对象,而不是直接处理UI事件。 UI事件处理程序只是查找绑定到事件的命令,然后调用集中的“执行命令X”方法,这将处理UI线程的所有编组等。

除了清理整个混乱之外,这还可以让您轻松地将关键绑定和脚本/自动化等内容添加到您的应用程序中,并使UI更具可扩展性和可维护性。

这是WCF中使用的核心命令调度方法并非巧合 - 如果您没有使用WCF,那么很遗憾,您需要实现等效的。实现一个基本的命令调度系统需要一些工作,但除非你的应用程序是微不足道的,否则你会很高兴你做到了。

答案 5 :(得分:0)

如果您有许多需要InvokeRequired检查的函数,这里有一个示例可以为您节省大量代码。你应该注意到一些重要的事情:

  • 我使用EventHandler<DownloaderProgressArgs>而不是为每个函数创建新的委托。
  • GetInvokeRequiredDelegate函数包装所有这些函数的代码。
  • 可以将此代码移动到静态类中,以便在多个表单之间共享,但这需要更多工作和不同的结构。就像在这里一样,函数只知道你正在处理哪种形式,因为函数存在于该形式中。

这是我设置用于测试GetInvokeRequiredDelegate&lt; T&gt;()的所有代码:

    public partial class Form1 : Form
    {
        public event EventHandler<DownloaderProgressArgs> OnDownloadProgress;
        public event EventHandler<DownloaderProgressArgs> OnDownloadSpeed;

        public Form1()
        {
            InitializeComponent();

            OnDownloadProgress += GetInvokeRequiredDelegate<DownloaderProgressArgs>(DownloadProgress);
            OnDownloadSpeed += GetInvokeRequiredDelegate<DownloaderProgressArgs>(DownloadSpeed);

            new System.Threading.Thread(Test).Start();
        }

        public void Test()
        {
            OnDownloadProgress(this, new DownloaderProgressArgs() { DownloadSpeed = 1000, speedOutput = 5 });
            OnDownloadSpeed(this, new DownloaderProgressArgs() { DownloadSpeed = 2000, speedOutput = 10 });
        }

        EventHandler<T> GetInvokeRequiredDelegate<T>(Action<object, T> action) where T : EventArgs
        {
            return ((o, e) =>
            {
                if (this.InvokeRequired)
                {
                    this.BeginInvoke(action, new object[] { o, e});
                } else 
                {
                    action(o, e);
                }
            });
        }


        void DownloadProgress(object sender, DownloaderProgressArgs d)
        {
            label2.Text = d.speedOutput.ToString();
        }

        void DownloadSpeed(object sender, DownloaderProgressArgs e)
        {
            string speed = "";
            speed = (e.DownloadSpeed / 1024).ToString() + "kb/s";
            label3.Text = speed;
        }
    }

    public class DownloaderProgressArgs : EventArgs {
        public int DownloadSpeed;
        public int speedOutput;
    }