使用来自C#中不同线程的Invoke访问类成员

注意 :系列的一部分: C#:从另一个类访问表单成员以及如何从C#中的另一个cs文件访问表单对象 。


你好,

想法是在TCP客户端接收/发送数据包时使用备忘录通知用户。

经过几次修复后,最合适的解决方案就是这个

public string TextValue { set { this.Memo.Text += value + "\n"; } } 

这就是它被调用的方式

  var form = Form.ActiveForm as Form1; if(form != null) form.TextValue = "Test asdasd"; 

但是,由于不安全的线程调用,调用代码会引发exception。我在msdn找到了一个解决方案,但我似乎无法获得他们在那里使用的方法。

这是我的翻拍,不起作用。

  private void SetTextMemo(string txt) { if(this.Memo.InvokeRequired) { this.Invoke(SetTextMemo,txt); //error here } else { this.Memo.Text += txt + "\n"; } } 

错误:

参数’1’:无法从’方法组’转换为’System.Delegate’

参数’2’:无法从’string’转换为’object []’

基本上,我正在尝试使用Invoke从另一个线程访问备忘录(或者更可能是说,在备忘录中添加文本)。我之前从未使用它,也许这就是我误解了我的错误的原因。

简单的方法是:

 this.Invoke((MethodInvoker)delegate { this.Memo.Text += txt + "\n"; }); 

它使用匿名方法来内联工作。 由于您希望在另一个线程上,您也可以只调用Invoke – 即使从UI线程也是安全的。

如果您正在使用C#3.0和3.5框架,请尝试以下操作

 if ( this.Memo.InvokeRequired ) { this.Invoke((Action)(() => SetTextMemo(txt))); } 

您的实现假定该方法不会无限递归,因为InvokeRequired属性的行为将阻止它。 这种假设可能是正确的,但编码函数以完全避免这种可能性是没有问题的。 这是我的建议:

  private void SetMemo(string txt) { Memo.Text = txt; } private delegate void MemoSetter(string txt); public void ThreadSafeSet(string txt) { Invoke(new MemoSetter(SetMemo), txt); } 

我曾经处理过所有这些跨线程的业务,但最近我选择了AOP,在那里你只需装饰一个在UI线程上执行的方法。 这是一个例子(来自PostSharp):

 public class FormsThreadAttribute : OnMethodInvocationAspect { public override void OnInvocation(MethodInvocationEventArgs eventArgs) { Form f = (Form)eventArgs.Delegate.Target; if (f.InvokeRequired) f.Invoke(eventArgs.Delegate, eventArgs.GetArgumentArray()); else eventArgs.Proceed(); } }