C# 自动化 InvokeRequired 代码模式
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2367718/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Automating the InvokeRequired code pattern
提问by Tom Corelis
I have become painfully aware of just how often one needs to write the following code pattern in event-driven GUI code, where
我已经痛苦地意识到人们需要在事件驱动的 GUI 代码中编写以下代码模式的频率,其中
private void DoGUISwitch() {
// cruisin for a bruisin' through exception city
object1.Visible = true;
object2.Visible = false;
}
becomes:
变成:
private void DoGUISwitch() {
if (object1.InvokeRequired) {
object1.Invoke(new MethodInvoker(() => { DoGUISwitch(); }));
} else {
object1.Visible = true;
object2.Visible = false;
}
}
This is an awkward pattern in C#, both to remember, and to type. Has anyone come up with some sort of shortcut or construct that automates this to a degree? It'd be cool if there was a way to attach a function to objects that does this check without having to go through all this extra work, like a object1.InvokeIfNecessary.visible = true
type shortcut.
这是 C# 中的一种尴尬模式,既要记住又要键入。有没有人想出某种捷径或构造来在一定程度上自动化?如果有一种方法可以将函数附加到执行此检查的对象而无需执行所有这些额外工作,例如object1.InvokeIfNecessary.visible = true
类型快捷方式,那将会很酷。
Previous answershave discussed the impracticality of just calling Invoke() every time, and even then the Invoke() syntax is both inefficient and stillawkward to deal with.
上一页答案已经讨论的只是打电话的invoke()每次不切实际,甚则调用()语法既效率低下,仍然尴尬应对。
So, has anyone figured out any shortcuts?
那么,有没有人想出任何捷径?
采纳答案by Olivier Jacot-Descombes
Lee's approach can be simplified further
Lee 的方法可以进一步简化
public static void InvokeIfRequired(this Control control, MethodInvoker action)
{
// See Update 2 for edits Mike de Klerk suggests to insert here.
if (control.InvokeRequired) {
control.Invoke(action);
} else {
action();
}
}
And can be called like this
并且可以这样称呼
richEditControl1.InvokeIfRequired(() =>
{
// Do anything you want with the control here
richEditControl1.RtfText = value;
RtfHelpers.AddMissingStyles(richEditControl1);
});
There is no need to pass the control as parameter to the delegate. C# automatically creates a closure.
无需将控件作为参数传递给委托。C# 自动创建一个闭包。
UPDATE:
更新:
According to several other posters Control
can be generalized as ISynchronizeInvoke
:
根据其他几张海报Control
可以概括为ISynchronizeInvoke
:
public static void InvokeIfRequired(this ISynchronizeInvoke obj,
MethodInvoker action)
{
if (obj.InvokeRequired) {
var args = new object[0];
obj.Invoke(action, args);
} else {
action();
}
}
DonBoitnott pointed out that unlike Control
the ISynchronizeInvoke
interface requires an object array for the Invoke
method as parameter list for the action
.
DonBoitnott指出,不同于Control
所述ISynchronizeInvoke
接口需要用于一个对象数组Invoke
方法作为参数列表action
。
UPDATE 2
更新 2
Edits suggested by Mike de Klerk (see comment in 1st code snippet for insert point):
Mike de Klerk 建议的编辑(请参阅第一个代码片段中的注释以获取插入点):
// When the form, thus the control, isn't visible yet, InvokeRequired returns false,
// resulting still in a cross-thread exception.
while (!control.Visible)
{
System.Threading.Thread.Sleep(50);
}
See ToolmakerSteve's comment below for concerns about this suggestion.
有关此建议的疑虑,请参阅下面 ToolmakerSteve 的评论。
回答by Lee
You could write an extension method:
你可以写一个扩展方法:
public static void InvokeIfRequired(this Control c, Action<Control> action)
{
if(c.InvokeRequired)
{
c.Invoke(new Action(() => action(c)));
}
else
{
action(c);
}
}
And use it like this:
并像这样使用它:
object1.InvokeIfRequired(c => { c.Visible = true; });
EDIT: As Simpzon points out in the comments you could also change the signature to:
编辑:正如 Simpzon 在评论中指出的,您还可以将签名更改为:
public static void InvokeIfRequired<T>(this T c, Action<T> action)
where T : Control
回答by Matt Davis
Here's the form I've been using in all my code.
这是我在所有代码中使用的表单。
private void DoGUISwitch()
{
Invoke( ( MethodInvoker ) delegate {
object1.Visible = true;
object2.Visible = false;
});
}
I've based this on the blog entry here. I have not had this approach fail me, so I see no reason to complicate my code with a check of the InvokeRequired
property.
我已经在博客条目基于此这里。我没有让这种方法失败,所以我认为没有理由通过检查InvokeRequired
属性来使我的代码复杂化。
Hope this helps.
希望这可以帮助。
回答by Aaron Gage
Create a ThreadSafeInvoke.snippet file, and then you can just select the update statements, right click and select 'Surround With...' or Ctrl-K+S:
创建一个 ThreadSafeInvoke.snippet 文件,然后您只需选择更新语句,右键单击并选择“环绕...”或 Ctrl-K+S:
<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippet Format="1.0.0" xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<Header>
<Title>ThreadsafeInvoke</Title>
<Shortcut></Shortcut>
<Description>Wraps code in an anonymous method passed to Invoke for Thread safety.</Description>
<SnippetTypes>
<SnippetType>SurroundsWith</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Code Language="CSharp">
<![CDATA[
Invoke( (MethodInvoker) delegate
{
$selected$
});
]]>
</Code>
</Snippet>
</CodeSnippet>
回答by stephan Schmuck
I'd rather use a single instance of a method Delegate instead of creating a new instance every time. In my case i used to show progress and (info/error) messages from a Backroundworker copying and casting large data from a sql instance. Everywhile after about 70000 progress and message calls my form stopped working and showing new messages. This didn't occure when i started using a single global instance delegate.
我宁愿使用 Delegate 方法的单个实例,而不是每次都创建一个新实例。在我的情况下,我曾经显示来自 Backroundworker 的进度和(信息/错误)消息,从 sql 实例复制和投射大数据。每次在大约 70000 个进度和消息调用之后,我的表单停止工作并显示新消息。当我开始使用单个全局实例委托时,这并没有发生。
delegate void ShowMessageCallback(string message);
private void Form1_Load(object sender, EventArgs e)
{
ShowMessageCallback showMessageDelegate = new ShowMessageCallback(ShowMessage);
}
private void ShowMessage(string message)
{
if (this.InvokeRequired)
this.Invoke(showMessageDelegate, message);
else
labelMessage.Text = message;
}
void Message_OnMessage(object sender, Utilities.Message.MessageEventArgs e)
{
ShowMessage(e.Message);
}
回答by Steve Wood
You should never be writing code that looks like this:
你永远不应该写这样的代码:
private void DoGUISwitch() {
if (object1.InvokeRequired) {
object1.Invoke(new MethodInvoker(() => { DoGUISwitch(); }));
} else {
object1.Visible = true;
object2.Visible = false;
}
}
If you do have code that looks like this then your application is not thread-safe. It means that you have code which is already calling DoGUISwitch() from a different thread. It's too late to be checking to see if it's in a different thread. InvokeRequire must be called BEFORE you make a call to DoGUISwitch. You should not access any method or property from a different thread.
如果您确实有类似这样的代码,那么您的应用程序就不是线程安全的。这意味着您的代码已经从不同的线程调用 DoGUISwitch()。现在检查它是否在不同的线程中为时已晚。InvokeRequire 必须在调用 DoGUISwitch 之前调用。您不应从不同的线程访问任何方法或属性。
Reference: Control.InvokeRequired Propertywhere you can read the following:
参考:Control.InvokeRequired 属性,您可以在其中阅读以下内容:
In addition to the InvokeRequired property, there are four methods on a control that are thread safe to call: Invoke, BeginInvoke, EndInvoke and CreateGraphics if the handle for the control has already been created.
除了 InvokeRequired 属性之外,控件上还有四种可以线程安全调用的方法:Invoke、BeginInvoke、EndInvoke 和 CreateGraphics(如果已经创建了控件的句柄)。
In a single CPU architecture there's no problem, but in a multi-CPU architecture you can cause part of the UI thread to be assigned to the processor where the calling code was running...and if that processor is different from where the UI thread was running then when the calling thread ends Windows will think that the UI thread has ended and will kill the application process i.e. your application will exit without error.
在单 CPU 架构中没有问题,但在多 CPU 架构中,您可能会将部分 UI 线程分配给运行调用代码的处理器……如果该处理器与 UI 线程所在的处理器不同正在运行,然后当调用线程结束时,Windows 会认为 UI 线程已经结束并会终止应用程序进程,即您的应用程序将退出而不会出错。
回答by gxtaillon
Here's an improved/combined version of Lee's, Oliver's and Stephan's answers.
这是 Lee、Oliver 和 Stephan 答案的改进/组合版本。
public delegate void InvokeIfRequiredDelegate<T>(T obj)
where T : ISynchronizeInvoke;
public static void InvokeIfRequired<T>(this T obj, InvokeIfRequiredDelegate<T> action)
where T : ISynchronizeInvoke
{
if (obj.InvokeRequired)
{
obj.Invoke(action, new object[] { obj });
}
else
{
action(obj);
}
}
The template allows for flexible and cast-less code which is much more readable while the dedicated delegate provides efficiency.
该模板允许灵活且无需强制转换的代码,在专用委托提供效率的同时,这些代码更具可读性。
progressBar1.InvokeIfRequired(o =>
{
o.Style = ProgressBarStyle.Marquee;
o.MarqueeAnimationSpeed = 40;
});
回答by Walter Verhoeven
I Kind of like to do it a bit different, i like to call "myself" if needed with an Action,
我有点喜欢做一些不同的事情,如果需要,我喜欢用 Action 称呼“我自己”,
private void AddRowToListView(ScannerRow row, bool suspend)
{
if (IsFormClosing)
return;
if (this.InvokeRequired)
{
var A = new Action(() => AddRowToListView(row, suspend));
this.Invoke(A);
return;
}
//as of here the Code is thread-safe
this is a handy pattern, the IsFormClosing is a field that i set to True when I am closing my form as there might be some background threads that are still running...
这是一个方便的模式,IsFormClosing 是我在关闭表单时设置为 True 的字段,因为可能有一些后台线程仍在运行...
回答by Konstantin S.
Usage:
用法:
control.InvokeIfRequired(c => c.Visible = false);
return control.InvokeIfRequired(c => {
c.Visible = value
return c.Visible;
});
Code:
代码:
using System;
using System.ComponentModel;
namespace Extensions
{
public static class SynchronizeInvokeExtensions
{
public static void InvokeIfRequired<T>(this T obj, Action<T> action)
where T : ISynchronizeInvoke
{
if (obj.InvokeRequired)
{
obj.Invoke(action, new object[] { obj });
}
else
{
action(obj);
}
}
public static TOut InvokeIfRequired<TIn, TOut>(this TIn obj, Func<TIn, TOut> func)
where TIn : ISynchronizeInvoke
{
return obj.InvokeRequired
? (TOut)obj.Invoke(func, new object[] { obj })
: func(obj);
}
}
}