繁体   English   中英

如何使用委托?

[英]How to use delegate?

我在不同的线程中使用委托。

这是工作:

delegate void AppendTextDelegate(string text);

 public void appendTextData(string text)
 {
        if (richBox1.InvokeRequired)
        {
            richBox1.Invoke(new AppendTextDelegate(appendTextData), new object[] { text           });
        }
        else
        {
            richBox1.AppendText(text);
        }
    }

这也是工作:

delegate void ChangeValDelegate(int value);

progressBar1.Invoke(new ChangeValDelegate((s) => progressBar1.Value = s), 0);

这是行不通的:

delegate void ClearDelegate();

listView.Invoke(new ClearDelegate()); <-- "Does not contain a constructor that takes 0 arguments"

我认为您正在创建大量代表来使用您的控件。 此外,当您只能使用其中一个控件(例如表单)时,您正在使用每个控件(listview、richtextbox ...)。

这里的问题是您必须在创建控件的同一线程中使用控件。 当您在不同的线程中运行代码时,您使用 InvokeRequired 来检查并在其他线程(主线程)中运行代码。

通常,您的所有控件和 forms 都是在主线程中创建的,因此您可以仅使用其中一个控件进行检查,以了解何时可以安全地访问这些控件。

在这里,您可以以更紧凑和简单的方式执行相同的操作。 首先,将此字段添加到您的表单中:

private static SynchronizationContext Context;

并在您的表单构造函数中设置它的值:

Context = SynchronizationContext.Current;

SynchronizationContext.Current它在不同的上下文中有所不同,但在表单构造器中,您知道上下文是表单上下文(您的主线程)。 因此,我们在Context中保存我们可以访问表单(及其所有控件)的上下文。

添加此方法以简化控件的工作:

private static void RunInMainThread(Action operation)
{
    if (Context != SynchronizationContext.Current)
    {
        Context.Post(o =>
        {
            try
            {
                operation();
            }
            catch (ObjectDisposedException)
            {
            }
        }, null);
    }
    else
    {
        operation();
    }
}

它类似于InvokeRequired但具有SynchronizationContext 如果您在正确的位置(与表单的上下文相同),只需运行该操作。 在其他情况下,在表单上下文中运行操作。 在这里您可以看到它是使用Post完成的。 通过这种方式,您可以避免死锁并接受代码可能会延迟运行(因为已入队)。

为什么是Action 因为您可以使用该操作运行任何东西,而将参数留在方法调用之外。 您不需要许多具有不同参数的方法来控制对控件的任何访问。

例如:

public void appendTextData(string text)
{
    RunInMainThread(() =>
    {
        richBox1.AppendText(text);
    });

    // Beeing a single line, you can compact the code
    //RunInMainThread(() => richBox1.AppendText(text));
}

您使用text参数运行AppendText ,但在RunInMainThread方法中不需要此参数。

另一个例子,设置进度条来启动和清除列表视图:

RunInMainThread(() =>
{
    progressBar1.Value = 0;
    listview.Clear();
});

关于代表:它们就像一个合同,一个接口。 代表没有代码,只告诉您有关 arguments 的信息......但没有代码。

在您的代码中:

progressBar1.Invoke(
    new ChangeValDelegate(
        (s) => progressBar1.Value = s), 0);

您正在运行与您的委托匹配的实现(返回 void 并获取 integer 作为参数)。

就好像:

var d = new ChangeValDelegate(OnChangeValDelegate);
BeginInvoke(d, new object[] { 0 });

有:

public void OnChangeValDelegate(int value)
{
    progressBar1.Value = value;
}

如您所见,您需要一个实现。 这里:

listView.Invoke(
    new ClearDelegate(/* NO IMPLEMENTATION */));

您需要清除列表视图的代码(匹配您的委托)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM