2010-08-04 5 views
6

У меня есть объект, который может занять от двух секунд до нескольких минут для инициализации. Причина заключается в том, что конструктор извлекает данные из веб-службы, которая может быть от нескольких килобайт до нескольких мегабайт, и в зависимости от скорости работы соединения пользователя скорость может сильно различаться. По этой причине я хочу помещать события в это, чтобы обрабатывать уведомление о ходе.Обработчики событий в конструкторах - возможно ли или даже мудро?

Вот мой вопрос: Могу ли я событие обработчиков в конструкторе или должен этот тип действия можно сделать с помощью метода в нагрузки?

Например:

public class MyObject 
{  
public event EventHandler<UpdateLoadProgressEventArgs> UpdateLoadProgress;  

public MyObject(int id) 
{ 
    Background worker bgWorker = new BackgroundWorker(); 
    bgWorker.DoWork += delegate(object s, DoWorkEventArgs args) 
    { 
     //load data and update progress incrementally 
     UpdateLoadProgress(this, new UpadteLoadProgressEventArgs(progressValue)); 

     Result = someValue;   
    } 
    bgWorker.RunWorkAsync(); 

} 

public int Result 
{ 
    get; 
    set; 
} 

} 

Однако, когда я пытаюсь связать обработчики событий конструктору они всегда нуль, когда называют:

MyObject o = new MyObject(1); 
o.UpdateLoadProgress += new EventHandler<EventArgs>(o_UpdateLoadProgress); 

Я предполагаю, что это происходит потому, что I провод события после конструктора. Единственная альтернатива, которую я вижу, это создать метод Load, который выполняет работу конструктора. Недостатком является то, что любой, кто использует этот класс, должен знать, чтобы вызвать Load, прежде чем пытаться получить доступ к Result (или любому другому свойству).

EDIT: Вот окончательное решение:

MyObjectBuilder Класс

public class MyObjectBuilder 
    { 
     public event ProgressChangedEventHandler ProgressChanged; 

     public MyObject CreateMyObject() 
     { 
      MyObject o = new MyObject(); 
      o.Load(ProgressChanged); 

      return o; 
     } 
    } 

MyObject Класс

public class MyObject 
    { 
     public int Result { get; set;} 

     public void Load(ProgressChangedEventHandler handler) 
     { 
      BackgroundWorker bgWorker = new BackgroundWorker(); 
      bgWorker.WorkerReportsProgress = true; 
      bgWorker.ProgressChanged += handler; 

      bgWorker.DoWork += delegate(object s, DoWorkEventArgs args) 
      { 
       for (int i = 0; i < 100; i++) 
       { 
        Thread.Sleep(10); 
        Result = i; 

        bgWorker.ReportProgress(i); 
       } 
      }; 
      bgWorker.RunWorkerAsync();      
     } 
    } 

Клас программы s

class Program 
    { 
     static void Main(string[] args) 
     { 
      MyObjectBuilder builder = new MyObjectBuilder(); 
      builder.ProgressChanged += new ProgressChangedEventHandler(builder_ProgressChanged);   

      MyObject o = builder.CreateMyObject(); 
      Console.ReadLine(); 
     } 

     static void builder_ProgressChanged(object sender, ProgressChangedEventArgs e) 
     { 
      Console.WriteLine(e.ProgressPercentage); 
     } 
    } 
+1

Обычно я обнаружил, что конструктор может загружать данные. Вы пожалеете об этом рано или поздно, и если вы проведете модульное тестирование, это будет «раньше». –

ответ

8

Другим вариантом было бы передать обработчики событий в конструктор, конечно, тоже.

Лично я стараюсь избегать делать что-то подобное внутри конструктора. Создание нового объекта обычно не должно начинаться с фоновых задач, IMO. Вместо этого вы можете поместить его в статический метод, который, конечно, может вызвать частный конструктор.

Вы также можете разделить свой класс на два - строитель, который готовит все (например, события), а затем класс «в полете или завершен», который имеет свойство Result. Вы бы назвали Start или что-то подобное в первом классе, чтобы получить экземпляр второго.

+0

@Jon Я попытался нанести удар по вашей идее использования класса Builder. Я не думаю, что понимаю концепцию.Я передаю обработчик события в MyObjectBuilder, а затем возвращаю MyObject обратно с помощью метода create. К сожалению, MyObject все еще не полностью загружен. Какие-нибудь ухищрения, которые вы могли бы внести в мой код? –

+0

@Blake: Я бы не стал беспокоиться о том, что событие является частью MyObject - я просто передаю обработчик в конструктор (из MyObjectBuilder.CreateMyObject). Тем не менее, похоже, что это должно в основном работать ... что происходит? (Обратите внимание, что вы смешиваете * оба * подхода в этом коде - если вы довольны тем, что ваш образец кода передал обработчик событий в начале, вы можете сделать все это одним типом, возможно, со статическим методом. Идея строителя заключалась в том, чтобы позволить вам построить его, добавить обработчики событий и * затем * вызвать метод Create.) –

+0

@Jon Основная проблема заключается в том, что в точке Console.WriteLine (o.Result) вывод равен 0 вместо ожидаемого 99 (после завершения цикла). Прогресс обновляется правильно, его просто, что объект не полностью инициализирован. Имеет ли объект полностью инициализированную цель дизайна, которую я должен иметь, или это понимается по шаблону строителя, который он все еще находится в процессе построения? –

1

Возможно ли это? Может быть. Это мудро? №

Наиболее очевидная причина заключается в том, что вам не гарантируется, что фоновый поток не будет выполняться до того, как обработчики событий будут подключены после построения объекта. Или, что еще хуже, некоторые из обработчиков событий могут быть подписаны, а другие нет.

Смежные вопросы