2009-10-02 2 views
4

Я не уверен, что лучше всего подходит для передачи событий по линии родительским классам и нуждается в некоторой обратной связи.Как пересылать асинхронные события родительским классам?

Приведенный ниже примерный пример иллюстрирует то, что я хочу достичь.

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(); 

Еще более конкретный пример:

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

Не хотите ли вы передать их ** вверх ** родителям? ;) –

ответ

3

Я думаю, что вы, возможно, только что реализовали это немного ... Похоже, вы пытаетесь использовать асинхронные операции. Даже если вы используете операции синхронизации, хотя, как правило, вы бы просто использовать методы обратного вызова вместо событий в таком случае, как это ...

Вот пример того, что можно изменить, чтобы использовать обратные вызовы здесь:

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

Это не имело бы смысл только что каждый автомобиль называют событие, которое вызывает событие на родительском списке. Я хотел бы сделать это примерно так:

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

Что делать, если я добавляю больше автомобилей после того, как EventHandler настроен с помощью «manager.Cars.Add ((sender, args)»? – magix

+0

Хм .... Хороший вопрос. Я мог бы придумать что-то лучше, повесить Здесь он отредактирован. – RCIX

+0

Это, конечно, не будет обрабатывать удаление событий, для чего вам нужно добавить метод Remove, аналогичный Add One, только он делает обратное. – RCIX

2

Это кажется разумным, но я не совсем уверен, что случай использования и как это будет использоваться.

У вас есть сильная концепция сдерживания, но я не совсем уверен, почему. Кроме того, это странно, что CarList «вроде», похоже, владеет отдельными автомобилями.

Кроме того, я не знаю, почему Test() в классе Car возвратит результат и поднимет мероприятие. Кажется, у вас есть два разных пути для возврата тех же данных. И класс менеджера кажется полностью избыточным с классом CarList с первого взгляда.

В чем проблема, которую вы на самом деле пытаетесь решить здесь? Это может помочь мне определить хорошее решение для него.

+0

Я обновил исходный вопрос с помощью более конкретный пример. Большая часть «тяжелого» кода запускается в рабочих потоках async, и им необходимо сообщать цепочку агрегатов, когда они завершают запуск/изменение состояния/выполнения и т. д. – magix