2010-11-18 39 views
0

我的问题涉及到事件以及我在课堂中触发事件的位置。这个类包装我的TCP功能,我使用TcpListener来实现这一点。我知道一些TCP的东西可以从下面的例子中会丢失,但我希望把事情尽量简单:从c中的AsyncCallback触发事件#

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."; 
    } 
} 

回答

0

虽然你没有张贴MyTcpClass2的代码,我敢肯定,我明白你在说什么。

不,我不会这样做的第二种方式。因为,例如,如果有其他事情需要同时绑定到该事件,那么您将强制它在另一个线程上运行。

总之,接收事件的方法应该负责在需要的任何线程上运行需要的任何代码。引发事件的机制应完全忽略接收器需要的任何奇怪线程化的东西。除了使多事件绑定场景复杂化之外,它还将跨线程调用的逻辑移至不属于它的类中。 MyTcpClass类应该专注于处理TCP客户端/服务器问题,而不是用Winforms线程来解决问题。

+0

所以,你建议第一个例子,我在哪里访问任何winform控件在MainForm使用调用事件处理程序的东西?我意识到MyTcpClass应该忽略它所在的线程,但MyTcpClass与您的客户端连接具有“具有”异步回调关系。我倾向于在线程中触发事件(忽略了哪个线程),MyTcpClass被创建。我看到你的观点并同意它,但是微软自己通过其带有progresschanged事件的“backgroundworker”模拟了一个类似的事件。 – 2010-11-18 14:43:44

+0

@Tom:问题是这个线程模型对于Winforms来说非常特殊 - 你可能甚至无法在创建类的线程上调用事件处理程序! (如果线程终止了怎么办?) – cdhowie 2010-11-18 18:01:10