2010-11-18 41 views
0

Ma question concerne les événements et l'endroit où je déclenche les événements de ma classe. Cette classe enveloppe ma fonctionnalité TCP et j'utilise TcpListener pour y parvenir. Je me rends compte des choses TCP peut être absent de l'exemple suivant, mais je veux faire des choses aussi simples que possible:événement déclencheur à partir d'un AsyncCallback dans C#

C# 2.0 échantillon

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 
    } 
} 

Je suppose que ma question est, sans casser ce modèle trop beaucoup, qu'est-ce qui a plus de sens? Aussi, si quelqu'un a une meilleure solution plus élégante, ils sont les bienvenus.

Théorie

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

Répondre

0

Alors que vous ne publiez pas le code pour MyTcpClass2, je suis sûr que je vois ce que vous voulez en venir.

Non, je ne le ferais pas de la deuxième façon. Parce que, par exemple, si quelque chose d'autre a besoin de se lier à cet événement en même temps, vous allez le forcer à tourner sur un autre thread. En bref, la méthode qui reçoit l'événement devrait être responsable de l'exécution de tout code dont il a besoin sur n'importe quel thread dont il a besoin. Le mécanisme qui déclenche l'événement devrait être complètement inconscient de toute sorte de trucs de threads étranges que le récepteur a besoin. En plus de compliquer le scénario de liaison multi-événements, il déplace la logique de l'invocation inter-thread dans une classe à laquelle elle n'appartient pas. La classe MyTcpClass devrait être axée sur la gestion des questions client/serveur TCP, et non sur les threads Winforms.

+0

Donc, vous suggérez le premier exemple, où j'accède à tout truc de contrôle de winform dans le gestionnaire d'événement dans MainForm en utilisant l'invocation? Je me rends compte que MyTcpClass devrait être inconscient du fil sur lequel il se trouve, mais MyTcpClass "Has An" relation de rappel asynchrone avec le client se connecter. Je préférerais déclencher l'événement dans le thread (inconscient à quel fil quoique) dans lequel MyTcpClass a été créé. Je vois votre point et je suis d'accord avec lui, mais Microsoft lui-même modélise un événement similaire avec son "backgroundworker" avec son événement progresschanged. –

+0

@Tom: Le problème est que ce modèle de thread est assez spécifique à Winforms - vous ne pourrez peut-être même pas invoquer le gestionnaire d'événements sur le thread sur lequel la classe a été créée! (Et si le fil se terminait?) – cdhowie