2010-11-18 61 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