从 C# 中的 AsyncCallback 触发事件
我的问题涉及事件以及我在课堂上触发事件的位置。这个类包装了我的 TCP 功能,我正在使用 TcpListener 来实现这一点。我意识到以下示例中可能缺少一些 TCP 内容,但我想让事情尽可能简单:
c# 2.0 示例
class MyTcpClass
{
public delegate void ClientConnectHandler(Socket client, int clientNum);
public event ClientConnectHandler ClientConnect;
private Socket wellKnownSocket;
private Socket[] clientSockets = new Socket[MAX_CLIENTS];
private int numConnected = 0;
private void OnClientConnect(Socket client, int clientNum)
{
if (ClientConnect != null)
ClientConnect(client, clientNum);
}
public void StartListening()
{
//initialize wellKnownSocket
//...
wellKnownSocket.BeginAccept(new AsyncCallback(internal_clientConnect);
}
public void internal_clientConnect(IAsyncResult ar)
{
//Add client socket to clientSocket[numConnected]
//numConnected++;
//...
wellKnownSocket.EndAccept(ar);
OnClientConnect(clientSocket[numConnected], numConnected);
//error: event happens on different thread!!
}
}
class MainForm
{
void Button_click()
{
MyTcpClass mtc = new MyTcpClass();
mtc.ClientConnect += mtc_ClientConnected;
}
void mtc_clientConnected(Socket client, int clientNum)
{
ActivityListBox.Items.Add("Client #" + clientNum.ToString() + " connected.");
//exception: cannot modify control on seperate thread
}
}
我想我的问题是,在不过多破坏此模式的情况下,什么更有意义?另外,如果有人有更好更优雅的解决方案,欢迎他们。
理论
class MainForm
{
public MainForm()
{
MyTcpClass mtc = new MyTcpClass();
MyTcpClass2 mtc2 = new MyTcpClass2(this);
//this version holds a Form handle to invoke the event
mtc.ClientConnect += mtc_uglyClientConnect;
mtc2.ClientConnect += mtc2_smartClientConnect;
}
//This event is being called in the AsyncCallback of MyTcpClass
//the main form handles invoking the control, I want to avoid this
void mtc_uglyClientConnect(Socket s, int n)
{
if (mycontrol.InvokeRequired)
{
//call begininvoke to update mycontrol
}
else
{
mycontrol.text = "Client " + n.ToString() + " connected.";
}
}
//This is slightly cleaner, as it is triggered in MyTcpClass2 by using its
//passed in Form handle's BeginInvoke to trigger the event on its own thread.
//However, I also want to avoid this because referencing a form in a seperate class
//while having it (the main form) observe events in the class as well seems... bad
void mtc2_smartClientConnect(Socket s, int n)
{
mycontrol.text = "Client " + n.ToString() + " connected.";
}
}
My question involves events and where I am triggering the events in my class. This class wraps my TCP functionality and I am using TcpListener to achieve this. I realize some TCP stuff may be missing from the following example but I want to make things as simple as possible:
c# 2.0 sample
class MyTcpClass
{
public delegate void ClientConnectHandler(Socket client, int clientNum);
public event ClientConnectHandler ClientConnect;
private Socket wellKnownSocket;
private Socket[] clientSockets = new Socket[MAX_CLIENTS];
private int numConnected = 0;
private void OnClientConnect(Socket client, int clientNum)
{
if (ClientConnect != null)
ClientConnect(client, clientNum);
}
public void StartListening()
{
//initialize wellKnownSocket
//...
wellKnownSocket.BeginAccept(new AsyncCallback(internal_clientConnect);
}
public void internal_clientConnect(IAsyncResult ar)
{
//Add client socket to clientSocket[numConnected]
//numConnected++;
//...
wellKnownSocket.EndAccept(ar);
OnClientConnect(clientSocket[numConnected], numConnected);
//error: event happens on different thread!!
}
}
class MainForm
{
void Button_click()
{
MyTcpClass mtc = new MyTcpClass();
mtc.ClientConnect += mtc_ClientConnected;
}
void mtc_clientConnected(Socket client, int clientNum)
{
ActivityListBox.Items.Add("Client #" + clientNum.ToString() + " connected.");
//exception: cannot modify control on seperate thread
}
}
I guess my question is, without breaking this pattern too much, what makes more sense? Also, if anyone has a better more elegant solution they are welcome.
Theory
class MainForm
{
public MainForm()
{
MyTcpClass mtc = new MyTcpClass();
MyTcpClass2 mtc2 = new MyTcpClass2(this);
//this version holds a Form handle to invoke the event
mtc.ClientConnect += mtc_uglyClientConnect;
mtc2.ClientConnect += mtc2_smartClientConnect;
}
//This event is being called in the AsyncCallback of MyTcpClass
//the main form handles invoking the control, I want to avoid this
void mtc_uglyClientConnect(Socket s, int n)
{
if (mycontrol.InvokeRequired)
{
//call begininvoke to update mycontrol
}
else
{
mycontrol.text = "Client " + n.ToString() + " connected.";
}
}
//This is slightly cleaner, as it is triggered in MyTcpClass2 by using its
//passed in Form handle's BeginInvoke to trigger the event on its own thread.
//However, I also want to avoid this because referencing a form in a seperate class
//while having it (the main form) observe events in the class as well seems... bad
void mtc2_smartClientConnect(Socket s, int n)
{
mycontrol.text = "Client " + n.ToString() + " connected.";
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
虽然您没有发布
MyTcpClass2
的代码,但我很确定我明白您的意思。不,我不会采取第二种方式。因为,例如,如果有其他任何事情需要同时绑定到该事件,您将强制它在另一个线程上运行。
简而言之,接收事件的方法应该负责在它需要的任何线程上运行它需要的任何代码。引发事件的机制应该完全忽略接收者所需的任何奇怪的线程内容。除了使多事件绑定场景变得复杂之外,它还将跨线程调用的逻辑移到了一个不属于它的类中。
MyTcpClass
类应该专注于处理 TCP 客户端/服务器事务,而不是与 Winforms 线程混在一起。While you did not post the code for
MyTcpClass2
, I'm pretty sure I see what you're getting at.No, I wouldn't do it the second way. Because, for example, if anything else ever needs to bind to that event at the same time, you are going to force it to run on another thread.
In short, the method that receives the event should be responsible for running whatever code it needs to on whatever thread it needs to. The mechanism that raises the event should be completely oblivious to any sort of weird threading stuff that the receiver requires. Aside from complicating the multiple-event-binding scenario, it moves the logic of cross-thread invocation into a class where it doesn't belong. The
MyTcpClass
class should be focused on handling TCP client/server matters, not mucking around with Winforms threads.