我有一个类似“ClientSocket.cs”
的类 class ClientSocket {
public delegate void ConnectHandler(object sender, EventArgs e);
public event ConnectHandler ConnectEvent = delegate { };
protected void OnConnectEvent(EventArgs e) {
ConnectHandler ev = ConnectEvent;
ev(this, e);
}
}
另一节课“myForm.cs”
public partial class myForm : Form {
private ClientSocket client;
private void button1_Click(object sender, EventArgs e) {
client = new ClientSocket();
client.ConnectEvent += myForm_OnConnectEvent;
client.connect();
}
// Handler for ConnectEvent
private void myForm_OnConnectEvent(object sender, EventArgs e) {
//this.BeginInvoke((MethodInvoker)delegate { writeLog("Connected"); });
writeLog("Connected");
}
// Function that write a log string to a TextBox
public writeLog(string log) {
guiTextBox.AppendText(log);
}
}
这里的问题。 我尝试用BeginInvoke调用writeLog或直接调用它。有时在写入guiTextBox时会出现InvalidOperationException。 我不明白为什么收到这条消息。该事件由ClientSocket对象触发,但事件处理程序相对于主UI线程(myForm)。
我可以避免为我班级的每个EventHandler使用BeginInvoke / Invoke吗?
编辑:我明白有什么不同,现在我试着了解调用此事件的最佳做法。
我应该在 RAISING BASE类中的事件(在这种情况下是ClientSocket)时放置BeginInvoke / Invoke方法
protected void OnConnectEvent(EventArgs e) {
ConnectHandler ev = ConnectEvent;
this.BeginInvoke((MethodInvoker)delegate { ev(this, e);});
}
或者我应该把那个当我正在使用该对象并向该处理程序添加一个侦听器
// Handler for ConnectEvent used in GUI (myForm)
private void myForm_OnConnectEvent(object sender, EventArgs e) {
this.BeginInvoke((MethodInvoker)delegate { writeLog("Connected"); });
}
干杯
答案 0 :(得分:3)
事件处理程序在myForm
中声明,但执行处理程序的线程由ClientSocket
类的逻辑定义。如果这将是后台线程,则将从后台线程引发事件处理程序,因此,您需要BeginInvoke
以避免对控件的跨线程访问。
换句话说:任何类型的任何方法的归属都与线程无关,线程将执行此方法。这些东西(类型和线程)是平行的宇宙。
顺便说一下,你可以替换它:
public delegate void ConnectHandler(object sender, EventArgs e);
public event ConnectHandler ConnectEvent = delegate { };
用这个:
public event EventHandler ConnectEvent;
无需再制作另一种代表类型。
答案 1 :(得分:3)
this.BeginInvoke
内的ClientSocket
不存在 。为了能够执行BeginInvoke
,必须在具有该方法的对象(在您的情况下为您的表单)上调用它。
如果您希望在ClientSocket类中进行调用,则需要传入具有Control
函数的BeginInvoke
。
但是,如果我在写这篇文章,我就不会这样做。它为ClientSocket
添加了一个不必要的要求,即 必须 传入Control
(这称为Tightly Coupling,您应该尝试在你的编程中避免它。我个人会让事件在它想要引发的任何线程中传递,让消费者担心做任何特殊的调用(如果他们甚至需要的话)。
以下是我将如何编写myForm_OnConnectEvent
,此模式检查是否需要调用,如果我们这样做,则使用相同的参数再次调用该函数,但这次是在UI线程上。
// Handler for ConnectEvent used in GUI (myForm)
private void myForm_OnConnectEvent(object sender, EventArgs e)
{
if(this.InvokeRequired)
{
this.BeginInvoke(new ConnectHandler(myForm_OnConnectEvent), new object[] {sender, e});
return;
}
writeLog("Connected");
}
作为旁注,我不知道writeLog
正在做什么(it should have a capital W by the way),但如果它没有与UI交互,则根本不需要进行任何调用。如果它与用户界面上的TextBox
或其他内容互动, 就是我要调用的地方。
private void myForm_OnConnectEvent(object sender, EventArgs e)
{
writeLog("Connected");
}
private void writeLog(string logMessage)
{
if(logTextBox.InvokeRequired)
{
logTextBox.BeginInvoke(new Action<string>(writeLog), logMessage);
return;
}
var logLine = String.Format("{0:g}: {1}{2}", DateTime.Now, logMessage, Enviorment.NewLine);
logTextBox.AppendText(logLine);
}