2009-10-02 9 views
4

Je ne suis pas sûr de la meilleure approche pour transmettre des événements à des classes parentes et avoir besoin de commentaires.Comment faire pour transférer des événements asynchrones aux classes parentes?

L'exemple de code ci-dessous tente d'illustrer ce que je veux réaliser.

namespace test { 
public delegate void TestCompletedEventHandler(object sender, 
    TestCompletedEventArgs e); 

    public class Manager { 
     CarList m_carlist = null; 

     public CarList Cars { 
      get { return m_carlist; } 
      set { m_carlist = value; } 
     } 

     public Manager() { 
      Cars = new CarList(this); 
     } 

     public void Report(bool successfull) { 
      //... 
     } 
    } 

    public class CarList : List<Car> { 
     protected internal event TestCompletedEventHandler 
      Car_TestCompleted = null; 

     protected readonly Manager m_manager = null; 

     public Manager Manager { 
      get { return m_manager; } 
     } 

     public CarList(Manager manager) { 
      m_manager = manager; 
     } 

     public void Test() { 
      foreach(Car car in this) { 
       bool ret = car.Test(); 
       manager.Report(ret); 
      } 
     } 

     public void Add(Car car) { 
      //Is this a good approach? 
      car.TestCompleted += 
       new TestCompletedEventHandler(Car_TestCompleted_Method); 
      base.Add(car); 
     } 

     private void Car_TestCompleted_Method(object sender, 
      TestCompletedEventArgs e) 
     { 
      if(Car_TestCompleted != null) Car_TestCompleted(sender, e); 
     } 
    } 

    public class Car { 
     protected internal event TestCompletedEventHandler 
      TestCompleted = null; 

     public bool Test() { 
      //... 

      if(TestCompleted != null) TestCompleted(this, 
       new TestCompletedEventArgs()) 
     } 
    } 

    public class TestCompletedEventArgs : EventArgs { 
     //... 
    } 
} 

using test; 
Manager manager = new Manager(); 
manager.Cars.Car_TestCompleted += 
    new TestCompletedEventHandler (Car_TestCompleted_Method); 
manager.Cars.Test(); 

Un autre exemple plus précis:

//Contains DataItems and interfaces for working with them 
class DataList 
{ 
    public List<DataItem> m_dataitems { get; set; } 
    public TestManager m_testmanager { get; set; } 
    // ... 
} 

class DataItem 
{ 
    // ... 
} 

//A manager class for running tests on a DataList 
class TestManager 
{ 
    public List<TestSource> m_sources { get; set; } 
    public WorkerManager m_workermanager { get; set; } 
    // ... 
} 

//A common interface for Tests 
abstract class TestSource 
{ 
    public event EventHandler<EventArgs<object>> Completed = null; 
    protected TestManager m_owner { get; set; } 

    public abstract void RunAsync(); 
    // ... 
} 

//A test 
class Test1 : TestSource 
{ 
    public virtual void RunAsync() 
    { 
     //Add commands 
     //Run workers 
     //Report progress to DataList and other listeners (like UI) 

     //Events seem like a bad approach since they need to be forwarded through many levels of abstraction 
     if(Completed != null) Completed(this, new EventArgs<object>(null)); 
    } 
    // ... 
} 

//Manages a number of workers and a queue of commands 
class WorkerManager 
{ 
    public List<MyWorker> m_workers { get; set; } 
    public Queue<Command> m_commands { get; set; } 
} 

//Wrapper for BackgroundWorker 
class MyWorker 
{ 
    // ... 
} 

//Async command 
interface Command 
{ 
    // ... 
} 
+1

Ne voudriez-vous pas les transmettre ** au ** parent? ;) –

Répondre

3

Je pense que vous avez peut-être mis en œuvre un peu plus ... Il semble que vous essayiez d'utiliser des opérations asynchrones. Même si vous utilisez des opérations de synchronisation si, en général, vous souhaitez simplement utiliser des méthodes de rappel au lieu d'événements dans un cas comme celui-ci ...

Voici un exemple de choses à changer pour utiliser les callbacks ici:

//new delegate 
public delegate void CarReportCallback(Car theCar, bool result); 

//in the Manager class, make report conform to delegate's signature 
public void Report(Car theCar, bool result) 
{ 
    //do something, you know which car and what the result is. 
} 

//in the CarList class pass a reference to the report method in 
public void Test() 
{ 
    foreach(Car car in this) 
    { 
     car.Test(manager.Report); 
    } 
} 

//in the Car class use the delegate passed to invoke the reporting 
public void Test(CarReportCallback callback) 
{ 
    //... do stuff 
    callback(this, isTestCompleted); 
} 
2

Il ne serait pas logique d'avoir juste chaque voiture appeler un événement qui appelle un événement sur la liste des parents. Je le ferais plus comme ceci:

namespace test { 
    public delegate void TestCompletedEventHandler(object sender, 
    TestCompletedEventArgs e); 

    public class Manager { 
     CarList m_carlist = null; 

     public CarList Cars { 
      get { return m_carlist; } 
      set { m_carlist = value; } 
     } 

     public Manager() { 
      Cars = new CarList(this); 
     } 

     public void Report(bool successful) { 
      //... 
     } 
    } 

    public class CarList : List<Car> { 
     protected readonly Manager m_manager = null; 
     protected List<Action<object, TestCompletedEventArgs>> delegatesList = new List<Action<object, TestCompletedEventArgs>>(); 

     public Manager Manager { 
      get { return m_manager; } 
     } 

     public CarList(Manager manager) { 
      m_manager = manager; 
     } 

     public void Test() { 
      foreach(Car car in this) { 
       bool ret = car.Test(); 
       manager.Report(ret); 
      } 
     } 
     public void Add(TestCompletedEventHandler e) { 
      foreach (Car car in this) { 
       car.OnTestCompleted += e; 
      } 
      delegatesList.Add(e); 
     } 
     public void Add(Car car) { 
     foreach(Action a in delegatesList) 
     { 
      car.OnTestCompleted += a; 
     } 
      base.Add(car); 
     } 
    } 

    public class Car { 
     protected internal event TestCompletedEventHandler OnTestCompleted = null; 

     public bool Test() { 
      //... 
      if (OnTestCompleted != null) OnTestCompleted(this, new TestCompletedEventArgs()); 
     } 
    } 

    public class TestCompletedEventArgs : EventArgs { 
     //... 
    } 
} 

using test; 
Manager manager = new Manager(); 
Manager.Cars.Add(new Car()); 
manager.Cars.Add(new Car()); 
manager.Cars.Add(new Car()); 
manager.Cars.Add((sender, args) => 
{ 
    //do whatever... 
}) 
manager.Cars.Test(); 
manager.Cars.Add(new Car()); 
+0

Que se passe-t-il si j'ajoute plus de voitures après que l'EventHandler est défini en utilisant "manager.Cars.Add ((expéditeur, args)"? – magix

+0

Hmm .... Bonne question. Je pourrais être en mesure de trouver quelque chose de mieux. Ceci est bien sûr, édité dans. – RCIX

+0

Ceci bien sûr ne gérera pas la suppression des événements, pour cela vous devez ajouter une méthode Remove similaire à la seule Ajouter une seule, elle fait l'inverse – RCIX

2

Il semble raisonnable, mais je ne suis pas vraiment sûr de ce que le cas d'utilisation est et comment cela serait utilisé.

Vous avez un fort concept de confinement, mais je ne sais pas trop pourquoi. De plus, c'est un peu bizarre que le CarList semble avoir la propriété des voitures individuelles.

En outre, je ne sais pas pourquoi Test() sur la classe Car renvoie tous les deux un résultat et déclencher un événement. Il semble que vous ayez deux chemins différents pour renvoyer les mêmes données. Et la classe Manager semble complètement redondante avec la classe CarList à première vue.

Quel est le problème que vous essayez de résoudre ici? Cela pourrait m'aider à définir une bonne solution.

+0

J'ai mis à jour la question origianal avec un Exemple plus spécifique La plus grande partie du code "lourd" est exécutée dans les threads de travail asynchrones et ils doivent signaler la chaîne d'agrégation lorsqu'ils terminent l'exécution/le changement d'état/la progression, etc. – magix

Questions connexes