2010-10-30 2 views
2

Рассмотрим код в следующем примере:Обойти виртуального вызова в конструкторе

abstract class Car 
{ 
    public abstract Engine CarEngine { get; protected set; } 

    public Car() 
    { 
     CarEngine.EngineOverheating += new EventHandler(CarEngine_EngineOverheating); 
    } 

    void CarEngine_EngineOverheating(object sender, EventArgs e) 
    { 
     // Subscribing to the event of all engines 
    } 
} 

sealed class Toyota : Car 
{ 
    public Toyota() 
    { 
     this.CarEngine = new ToyotaEngine(); 
    } 


    public override Engine CarEngine { get; protected set; } 
} 

abstract class Engine 
{ 
    public event EventHandler EngineOverheating; 
} 

class ToyotaEngine : Engine 
{ 

} 

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

Несколько вариантов, которые я вижу их минусы:

  1. Подписаться на каждом дочернем классе вручную - результаты с большим количеством кода котла пластины и, как следствие, к ошибкам.
  2. Функция Initialize() - непринужденная и избыточная.

Я был бы рад увидеть больше идей.

Спасибо!

ответ

7

Создать конструктор в автомобиле, принимая двигатель в качестве параметра и назначить его до подписки на события, как это:

abstract class Car 
{ 
    public abstract Engine CarEngine { get; protected set; } 

    public Car(Engine carEngine) 
    { 
     CarEngine = carEngine; 
     CarEngine.EngineOverheating += new EventHandler(CarEngine_EngineOverheating); 
    } 

    void CarEngine_EngineOverheating(object sender, EventArgs e) 
    { 
     // Subscribing to the event of all engines 
    } 
} 

sealed class Toyota : Car 
{ 
    public Toyota() 
     : base(new ToyotaEngine()) 
    { 
    } 


    public override Engine CarEngine { get; protected set; } 
} 
+0

Спасибо. Я пойду с этим :) – VitalyB

0

Вы можете попробовать пример ниже. Это устанавливает двигатель при первом доступе.

sealed class Toyota : Car 
{ 
    public Toyota() 
    { 
    } 

    public override Engine CarEngine 
    { 
     get 
     { 
      var engine = base.CarEngine; 

      if (engine == null) 
      { 
       engine = new ToyotaEngine(); 

       base.CarEngine = engine; 
      } 

      return engine; 
     } 
    } 
} 
+0

и _how_ знает конструктор 'Car', какой подкласс он создает, и поэтому он должен использовать CarEngine'? – xtofl

+0

Компилятор позаботится об этом для вас. Поскольку вы говорите «новая Toyota()», она автоматически использует эту перегрузку «CarEngine». –

3

вы не можете передать объект Engine на CTOR Автомобиля, который будет затем установить свойство (можно было бы сделать сеттер частным тогда) и зарегистрировать обработчик событий?

+0

Да, вот что я сделал. Благодаря! – VitalyB

0

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

Инстанцирование может выглядеть следующим образом

new ToyotaEngine(myToyotaCarInstance). 

I-й чернила, это совершенно верно для двигателя, чтобы иметь ссылку на автомобиль.

+0

Я бы предпочел, чтобы движок общался только событиями. Ребенок, который должен иметь ссылку на своих родителей, делает меня нервным :) – VitalyB

+0

Я думаю, что двунаправленные отношения - это путь. Делает жизнь намного проще. В вашем сценарии: может ли быть двигатель без автомобиля? Или автомобиль, без двигателя? Влияет ли такая потеря связи на ваш сценарий? Я сомневаюсь, это похоже на субоптимальную объектную модель для меня! – Falcon

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