[英]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.